| /** |
| * @license |
| * Copyright 2017 Google LLC |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| import '../gr-label-score-row/gr-label-score-row'; |
| import '../../../styles/shared-styles'; |
| import {LitElement, css, html} from 'lit'; |
| import {customElement, property} from 'lit/decorators.js'; |
| import { |
| ChangeInfo, |
| AccountInfo, |
| LabelNameToValueMap, |
| } from '../../../types/common'; |
| import {GrLabelScoreRow} from '../gr-label-score-row/gr-label-score-row'; |
| import { |
| getTriggerVotes, |
| computeLabels, |
| Label, |
| computeOrderedLabelValues, |
| getDefaultValue, |
| getApplicableLabels, |
| } from '../../../utils/label-util'; |
| import {ChangeStatus} from '../../../constants/constants'; |
| import {fontStyles} from '../../../styles/gr-font-styles'; |
| import {LabelNameToValuesMap} from '../../../api/rest-api'; |
| |
| @customElement('gr-label-scores') |
| export class GrLabelScores extends LitElement { |
| @property({type: Object}) |
| permittedLabels?: LabelNameToValuesMap; |
| |
| @property({type: Object}) |
| change?: ChangeInfo; |
| |
| @property({type: Object}) |
| account?: AccountInfo; |
| |
| static override get styles() { |
| return [ |
| fontStyles, |
| css` |
| .scoresTable { |
| display: table; |
| width: 100%; |
| table-layout: fixed; |
| } |
| .mergedMessage, |
| .abandonedMessage { |
| font-style: italic; |
| text-align: center; |
| } |
| .permissionMessage { |
| color: var(--deemphasized-text-color); |
| padding-left: var(--label-score-padding-left, 0); |
| } |
| gr-label-score-row:hover { |
| background-color: var(--hover-background-color); |
| } |
| gr-label-score-row { |
| display: table-row; |
| } |
| .heading-4 { |
| padding-left: var(--label-score-padding-left, 0); |
| margin-bottom: var(--spacing-s); |
| margin-top: var(--spacing-l); |
| } |
| .heading-4:first-of-type { |
| margin-top: 0; |
| } |
| `, |
| ]; |
| } |
| |
| override render() { |
| return html`${this.renderSubmitReqsLabels()}${this.renderTriggerVotes()} |
| ${this.renderErrorMessages()}`; |
| } |
| |
| private renderSubmitReqsLabels() { |
| const triggerVotes = getTriggerVotes(this.change); |
| const applicableLabels = getApplicableLabels(this.change); |
| const labels = computeLabels(this.account, this.change) |
| .filter(label => !triggerVotes.includes(label.name)) |
| .filter(label => applicableLabels.includes(label.name)); |
| if (!labels.length) return; |
| if ( |
| labels.filter( |
| label => !this.permittedLabels || this.permittedLabels[label.name] |
| ).length === 0 |
| ) { |
| return html`<h3 class="heading-4">Submit requirements votes</h3> |
| <div class="permissionMessage">You don't have permission to vote</div>`; |
| } |
| return html`<h3 class="heading-4">Submit requirements votes</h3> |
| ${this.renderLabels(labels)}`; |
| } |
| |
| private renderTriggerVotes() { |
| const triggerVotes = getTriggerVotes(this.change); |
| const labels = computeLabels(this.account, this.change).filter(label => |
| triggerVotes.includes(label.name) |
| ); |
| if (!labels.length) return; |
| if ( |
| labels.filter( |
| label => !this.permittedLabels || this.permittedLabels[label.name] |
| ).length === 0 |
| ) { |
| return html`<h3 class="heading-4">Trigger Votes</h3> |
| <div class="permissionMessage">You don't have permission to vote</div>`; |
| } |
| return html`<h3 class="heading-4">Trigger Votes</h3> |
| ${this.renderLabels(labels)}`; |
| } |
| |
| private renderLabels(labels: Label[]) { |
| return html`<div class="scoresTable"> |
| ${labels |
| .filter( |
| label => |
| this.permittedLabels?.[label.name] && |
| this.permittedLabels?.[label.name].length > 0 |
| ) |
| .map( |
| label => html`<gr-label-score-row |
| .label=${label} |
| .name=${label.name} |
| .labels=${this.change?.labels} |
| .permittedLabels=${this.permittedLabels} |
| .orderedLabelValues=${computeOrderedLabelValues( |
| this.permittedLabels |
| )} |
| ></gr-label-score-row>` |
| )} |
| </div>`; |
| } |
| |
| private renderErrorMessages() { |
| return html`<div |
| class="mergedMessage" |
| ?hidden=${this.change?.status !== ChangeStatus.MERGED} |
| > |
| Because this change has been merged, votes may not be decreased. |
| </div> |
| <div |
| class="abandonedMessage" |
| ?hidden=${this.change?.status !== ChangeStatus.ABANDONED} |
| > |
| Because this change has been abandoned, you cannot vote. |
| </div>`; |
| } |
| |
| getLabelValues(includeDefaults = true): LabelNameToValueMap { |
| const labels: LabelNameToValueMap = {}; |
| if (this.shadowRoot === null || !this.change) { |
| return labels; |
| } |
| for (const label of Object.keys(this.permittedLabels ?? {})) { |
| const selectorEl = this.shadowRoot.querySelector<GrLabelScoreRow>( |
| `gr-label-score-row[name="${label}"]` |
| ); |
| if (!selectorEl?.selectedItem) continue; |
| |
| const selectedVal = |
| typeof selectorEl.selectedValue === 'string' |
| ? Number(selectorEl.selectedValue) |
| : selectorEl.selectedValue; |
| |
| if (selectedVal === undefined) continue; |
| |
| const defValNum = getDefaultValue(this.change?.labels, label); |
| if (includeDefaults || selectedVal !== defValNum) { |
| labels[label] = selectedVal; |
| } |
| } |
| return labels; |
| } |
| } |
| |
| declare global { |
| interface HTMLElementTagNameMap { |
| 'gr-label-scores': GrLabelScores; |
| } |
| } |