Submit requirements - reviewer chip vote
The design is close to final design, but it will be polished later.
After: https://imgur.com/a/qp0p8go
Change-Id: I68ac379f28dfbb34e54e8dadbbaf0876ba47e285
diff --git a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.ts b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.ts
index ffcafdd..61785fc 100644
--- a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.ts
+++ b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.ts
@@ -16,6 +16,7 @@
*/
import '../../shared/gr-account-chip/gr-account-chip';
import '../../shared/gr-button/gr-button';
+import '../../shared/gr-vote-chip/gr-vote-chip';
import '../../../styles/shared-styles';
import {dom, EventApi} from '@polymer/polymer/lib/legacy/polymer.dom';
import {PolymerElement} from '@polymer/polymer/polymer-element';
@@ -33,6 +34,8 @@
DetailedLabelInfo,
EmailAddress,
AccountDetailInfo,
+ isDetailedLabelInfo,
+ LabelInfo,
} from '../../../types/common';
import {PolymerDeepPropertyChange} from '@polymer/polymer/interfaces';
import {GrAccountChip} from '../../shared/gr-account-chip/gr-account-chip';
@@ -41,6 +44,7 @@
import {ReviewerState} from '../../../constants/constants';
import {appContext} from '../../../services/app-context';
import {fireAlert} from '../../../utils/event-util';
+import {getApprovalInfo, getCodeReviewLabel} from '../../../utils/label-util';
@customElement('gr-reviewer-list')
export class GrReviewerList extends PolymerElement {
@@ -197,6 +201,20 @@
return maxScores.join(', ');
}
+ _computeVote(
+ reviewer: AccountInfo,
+ change?: ChangeInfo
+ ): ApprovalInfo | undefined {
+ const codeReviewLabel = this._computeCodeReviewLabel(change);
+ if (!codeReviewLabel || !isDetailedLabelInfo(codeReviewLabel)) return;
+ return getApprovalInfo(codeReviewLabel, reviewer);
+ }
+
+ _computeCodeReviewLabel(change?: ChangeInfo): LabelInfo | undefined {
+ if (!change || !change.labels) return;
+ return getCodeReviewLabel(change.labels);
+ }
+
@observe('change.reviewers.*', 'change.owner')
_reviewersChanged(
changeRecord: PolymerDeepPropertyChange<Reviewers, Reviewers>,
diff --git a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_html.ts b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_html.ts
index ca8bf87..c579a59 100644
--- a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_html.ts
+++ b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_html.ts
@@ -70,6 +70,11 @@
voteable-text="[[_computeVoteableText(reviewer, change)]]"
removable="[[_computeCanRemoveReviewer(reviewer, mutable)]]"
>
+ <gr-vote-chip
+ slot="vote-chip"
+ vote="[[_computeVote(reviewer, change)]]"
+ label="[[_computeCodeReviewLabel(change)]]"
+ ></gr-vote-chip>
</gr-account-chip>
</template>
<div class="controlsContainer" hidden$="[[!mutable]]">
diff --git a/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.ts b/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.ts
index f703037..670302f 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.ts
+++ b/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.ts
@@ -178,6 +178,7 @@
.voteableText=${this.voteableText}
>
</gr-account-link>
+ <slot name="vote-chip"></slot>
<gr-button
id="remove"
link=""
diff --git a/polygerrit-ui/app/elements/shared/gr-vote-chip/gr-vote-chip.ts b/polygerrit-ui/app/elements/shared/gr-vote-chip/gr-vote-chip.ts
new file mode 100644
index 0000000..789637c
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-vote-chip/gr-vote-chip.ts
@@ -0,0 +1,100 @@
+/**
+ * @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 {css, customElement, html, property} from 'lit-element';
+import {ApprovalInfo, LabelInfo} from '../../../api/rest-api';
+import {appContext} from '../../../services/app-context';
+import {KnownExperimentId} from '../../../services/flags/flags';
+import {getVotingRangeOrDefault, valueString} from '../../../utils/label-util';
+import {GrLitElement} from '../../lit/gr-lit-element';
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'gr-vote-chip': GrVoteChip;
+ }
+}
+
+@customElement('gr-vote-chip')
+export class GrVoteChip extends GrLitElement {
+ @property({type: Object})
+ vote?: ApprovalInfo;
+
+ @property({type: Object})
+ label?: LabelInfo;
+
+ private readonly flagsService = appContext.flagsService;
+
+ static get styles() {
+ return [
+ css`
+ .chipVote {
+ display: flex;
+ justify-content: center;
+ margin-right: var(--spacing-s);
+ padding: 1px;
+ border-radius: var(--border-radius);
+ color: var(--vote-text-color);
+ border: 1px solid var(--border-color);
+ line-height: calc(var(--line-height-normal) - 4px);
+ }
+ .max {
+ background-color: var(--vote-color-approved);
+ }
+ .min {
+ background-color: var(--vote-color-rejected);
+ }
+ .positive {
+ background-color: var(--vote-color-recommended);
+ border: 1px solid var(--vote-outline-recommended);
+ color: var(--chip-color);
+ }
+ .negative {
+ background-color: var(--vote-color-disliked);
+ border: 1px solid var(--vote-outline-disliked);
+ color: var(--chip-color);
+ }
+ `,
+ ];
+ }
+
+ render() {
+ if (!this.flagsService.isEnabled(KnownExperimentId.SUBMIT_REQUIREMENTS_UI))
+ return;
+ if (!this.vote?.value) return;
+ const className = this.computeClass(this.vote.value, this.label);
+ return html`<div class="chipVote ${className}">
+ ${valueString(this.vote.value)}
+ </div>`;
+ }
+
+ computeClass(vote: Number, label?: LabelInfo) {
+ const votingRange = getVotingRangeOrDefault(label);
+ if (vote > 0) {
+ if (vote === votingRange.max) {
+ return 'max';
+ } else {
+ return 'positive';
+ }
+ } else if (vote < 0) {
+ if (vote === votingRange.min) {
+ return 'min';
+ } else {
+ return 'negative';
+ }
+ }
+ return '';
+ }
+}
diff --git a/polygerrit-ui/app/utils/label-util.ts b/polygerrit-ui/app/utils/label-util.ts
index bcc92e8..92bbdac 100644
--- a/polygerrit-ui/app/utils/label-util.ts
+++ b/polygerrit-ui/app/utils/label-util.ts
@@ -20,6 +20,7 @@
DetailedLabelInfo,
isDetailedLabelInfo,
LabelInfo,
+ LabelNameToInfoMap,
VotingRangeInfo,
} from '../types/common';
@@ -102,3 +103,14 @@
return labelName1.localeCompare(labelName2);
}
+
+export function getCodeReviewLabel(
+ labels: LabelNameToInfoMap
+): LabelInfo | undefined {
+ for (const label of Object.keys(labels)) {
+ if (label === CODE_REVIEW) {
+ return labels[label];
+ }
+ }
+ return;
+}