Merge changes I632f940f,I86b22613,Ie3c56cb3,I02980c11,Ib59d55e7, ...
* changes:
Improve readability of copyWithListOfFilesUnchangedButAddedMergeList test
Add missing license headers
Fix bad formatting of license headers
Approval Copying: Improve wording of uploaderin description
StickyApprovalsIT: Add test for uploaderin condition
StickyApprovalsIT: Fix copyWithListOfFilesUnchangedButAddedMergeList test
StickyApprovalsIT: Add test for approverin condition
diff --git a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.ts b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.ts
index c3bb1bd..7a80396 100644
--- a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.ts
+++ b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.ts
@@ -119,11 +119,14 @@
? html`<a href=${this.computeGroupUrl(audit.member)}
>${this.getNameForGroup(audit.member)}</a
>`
- : html`<gr-account-label .account=${audit.member}></gr-account-label
+ : html`<gr-account-label
+ .account=${audit.member}
+ clickable
+ ></gr-account-label
>${this.getIdForUser(audit.member)}`}
</td>
<td class="by-user">
- <gr-account-label .account=${audit.user}></gr-account-label>
+ <gr-account-label clickable .account=${audit.user}></gr-account-label>
${this.getIdForUser(audit.user)}
</td>
</tr>
diff --git a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.ts b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.ts
index 70637b7..ad90759 100644
--- a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.ts
+++ b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.ts
@@ -269,7 +269,7 @@
return html`
<tr>
<td class="nameColumn">
- <gr-account-label .account=${member}></gr-account-label>
+ <gr-account-label .account=${member} clickable></gr-account-label>
</td>
<td>${member.email}</td>
<td class="deleteColumn">
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.ts b/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.ts
index 9fe3de9..d72f916 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.ts
+++ b/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.ts
@@ -342,7 +342,7 @@
return html`
<div class="tagger">
- <gr-account-label .account=${tagger}> </gr-account-label>
+ <gr-account-label .account=${tagger} clickable> </gr-account-label>
(<gr-date-formatter withTooltip .dateStr=${tagger.date}>
</gr-date-formatter
>)
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-action-bar/gr-change-list-action-bar.ts b/polygerrit-ui/app/elements/change-list/gr-change-list-action-bar/gr-change-list-action-bar.ts
index bd50407..342280c 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-action-bar/gr-change-list-action-bar.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-action-bar/gr-change-list-action-bar.ts
@@ -11,7 +11,6 @@
import {pluralize} from '../../../utils/string-util';
import {subscribe} from '../../lit/subscription-controller';
import '../../shared/gr-button/gr-button';
-import '../gr-change-list-mark-active-flow/gr-change-list-mark-active-flow';
import '../gr-change-list-bulk-abandon-flow/gr-change-list-bulk-abandon-flow';
/**
@@ -108,7 +107,6 @@
: nothing}
</div>
<div class="actionButtons">
- <gr-change-list-mark-active-flow></gr-change-list-mark-active-flow>
<gr-change-list-bulk-abandon-flow>
</gr-change-list-bulk-abandon-flow>
</div>
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-action-bar/gr-change-list-action-bar_test.ts b/polygerrit-ui/app/elements/change-list/gr-change-list-action-bar/gr-change-list-action-bar_test.ts
index c4cca02..ac648f5 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-action-bar/gr-change-list-action-bar_test.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-action-bar/gr-change-list-action-bar_test.ts
@@ -64,7 +64,6 @@
<span>1 change selected</span>
</div>
<div class="actionButtons">
- <gr-change-list-mark-active-flow></gr-change-list-mark-active-flow>
<gr-change-list-bulk-abandon-flow>
</gr-change-list-bulk-abandon-flow>
</div>
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.ts b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.ts
index cb41851..77602d0 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.ts
@@ -394,6 +394,7 @@
<td class="cell owner">
<gr-account-label
highlightAttention
+ clickable
.change=${this.change}
.account=${this.change?.owner}
></gr-account-label>
@@ -424,6 +425,7 @@
private renderChangeReviewers(reviewer: AccountInfo, index: number) {
return html`
<gr-account-label
+ clickable
hideAvatar
firstName
highlightAttention
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_test.ts b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_test.ts
index 1ddc604..b345513 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_test.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_test.ts
@@ -588,7 +588,11 @@
</div>
</a>
<span class="placeholder"> -- </span>
- <gr-account-label deselected="" highlightattention=""></gr-account-label>
+ <gr-account-label
+ deselected=""
+ clickable=""
+ highlightattention=""
+ ></gr-account-label>
<div></div>
<span></span>
<a class="fullRepo" href=""> test-project </a>
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-mark-active-flow/gr-change-list-mark-active-flow.ts b/polygerrit-ui/app/elements/change-list/gr-change-list-mark-active-flow/gr-change-list-mark-active-flow.ts
deleted file mode 100644
index 83c5935..0000000
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-mark-active-flow/gr-change-list-mark-active-flow.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-/**
- * @license
- * Copyright 2022 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-import {html, LitElement} from 'lit';
-import {customElement, query, state} from 'lit/decorators';
-import {bulkActionsModelToken} from '../../../models/bulk-actions/bulk-actions-model';
-import {resolve} from '../../../models/dependency';
-import {ChangeInfo} from '../../../types/common';
-import {subscribe} from '../../lit/subscription-controller';
-import '../../shared/gr-overlay/gr-overlay';
-import {GrOverlay} from '../../shared/gr-overlay/gr-overlay';
-
-@customElement('gr-change-list-mark-active-flow')
-export class GrChangeListMarkActiveFlow extends LitElement {
- @query('gr-overlay')
- private overlay!: GrOverlay;
-
- private readonly getBulkActionsModel = resolve(this, bulkActionsModelToken);
-
- @state() selectedChanges: ChangeInfo[] = [];
-
- override connectedCallback(): void {
- super.connectedCallback();
- subscribe(
- this,
- this.getBulkActionsModel().selectedChanges$,
- selectedChanges => {
- this.selectedChanges = selectedChanges;
- }
- );
- }
-
- override render() {
- const changesStr = this.selectedChanges.map(c => c._number).join(', ');
- return html`
- <gr-button
- ?disabled=${!this.isEnabled()}
- flatten
- @click=${() => this.overlay.open()}
- >mark as active</gr-button
- >
- <gr-overlay>
- <gr-dialog @cancel=${() => this.overlay.close()}>
- <div slot="header">Mark Changes as Active</div>
- <div slot="main">
- <div>Selected changes: ${changesStr}</div>
- </div>
- </gr-dialog>
- </gr-overlay>
- `;
- }
-
- private isEnabled(): boolean {
- // TODO: This is sample enable logic. Normally we would also use permissions
- // from the model data somehow.
- if (this.selectedChanges.length === 0) return false;
- return true;
- }
-}
-declare global {
- interface HTMLElementTagNameMap {
- 'gr-change-list-mark-active-flow': GrChangeListMarkActiveFlow;
- }
-}
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-mark-active-flow/gr-change-list-mark-active-flow_test.ts b/polygerrit-ui/app/elements/change-list/gr-change-list-mark-active-flow/gr-change-list-mark-active-flow_test.ts
deleted file mode 100644
index ef914d8..0000000
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-mark-active-flow/gr-change-list-mark-active-flow_test.ts
+++ /dev/null
@@ -1,99 +0,0 @@
-/**
- * @license
- * Copyright 2022 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-import {fixture, html} from '@open-wc/testing-helpers';
-import {
- BulkActionsModel,
- bulkActionsModelToken,
-} from '../../../models/bulk-actions/bulk-actions-model';
-import {wrapInProvider} from '../../../models/di-provider-element';
-import {getAppContext} from '../../../services/app-context';
-import '../../../test/common-test-setup-karma';
-import {createChange} from '../../../test/test-data-generators';
-import {queryAndAssert, waitUntilObserved} from '../../../test/test-utils';
-import {ChangeInfo, NumericChangeId} from '../../../types/common';
-import {GrButton} from '../../shared/gr-button/gr-button';
-import {GrOverlay} from '../../shared/gr-overlay/gr-overlay';
-import './gr-change-list-mark-active-flow';
-import type {GrChangeListMarkActiveFlow} from './gr-change-list-mark-active-flow';
-
-const change1 = {...createChange(), _number: 1 as NumericChangeId};
-const change2 = {...createChange(), _number: 2 as NumericChangeId};
-
-suite('gr-change-list-mark-active-flow tests', () => {
- let element: GrChangeListMarkActiveFlow;
- let model: BulkActionsModel;
-
- async function selectChange(change: ChangeInfo) {
- model.addSelectedChangeNum(change._number);
- await waitUntilObserved(model.selectedChangeNums$, selectedChangeNums =>
- selectedChangeNums.includes(change._number)
- );
- await element.updateComplete;
- }
-
- setup(async () => {
- model = new BulkActionsModel(getAppContext().restApiService);
- model.sync([change1, change2]);
-
- element = (
- await fixture(
- wrapInProvider(
- html`<gr-change-list-mark-active-flow></gr-change-list-mark-active-flow>`,
- bulkActionsModelToken,
- model
- )
- )
- ).querySelector('gr-change-list-mark-active-flow')!;
- await selectChange(change1);
- await selectChange(change2);
- await element.updateComplete;
- });
-
- test('renders flow', async () => {
- expect(element).shadowDom.to.equal(/* HTML */ `
- <gr-button flatten="" aria-disabled="false" role="button" tabindex="0"
- >mark as active</gr-button
- >
- <gr-overlay aria-hidden="true" style="outline: none; display: none;">
- <gr-dialog role="dialog">
- <div slot="header">Mark Changes as Active</div>
- <div slot="main">
- <div>Selected changes: 1, 2</div>
- </div>
- </gr-dialog>
- </gr-overlay>
- `);
- });
-
- test('button enabled when changes selected', async () => {
- const button = queryAndAssert<GrButton>(element, 'gr-button');
- assert.isFalse(button.disabled);
- });
-
- test('button disabled when no changes selected', async () => {
- model.clearSelectedChangeNums();
- await waitUntilObserved(model.selectedChangeNums$, s => s.length === 0);
- await element.updateComplete;
-
- const button = queryAndAssert<GrButton>(element, 'gr-button');
- assert.isTrue(button.disabled);
- });
-
- test('overlay hidden before button clicked', async () => {
- const overlay = queryAndAssert<GrOverlay>(element, 'gr-overlay');
- assert.isFalse(overlay.opened);
- });
-
- test('button click shows overlay', async () => {
- const button = queryAndAssert<GrButton>(element, 'gr-button');
-
- button.click();
- await element.updateComplete;
-
- const overlay = queryAndAssert<GrOverlay>(element, 'gr-overlay');
- assert.isTrue(overlay.opened);
- });
-});
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts
index 2456170..7625756 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts
@@ -581,14 +581,6 @@
}, createDefaultPatchChange());
}
- _getDiffPreferences() {
- return this.restApiService.getDiffPreferences();
- }
-
- _getPreferences() {
- return this.restApiService.getPreferences();
- }
-
// private but used in test
_toggleFileExpanded(file: PatchSetFile) {
// Is the path in the list of expanded diffs? If so, remove it, otherwise
diff --git a/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores.ts b/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores.ts
index 6035b6f..993f811 100644
--- a/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores.ts
+++ b/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores.ts
@@ -24,7 +24,6 @@
ChangeInfo,
AccountInfo,
DetailedLabelInfo,
- LabelNameToInfoMap,
LabelNameToValuesMap,
} from '../../../types/common';
import {
@@ -35,8 +34,8 @@
import {getAppContext} from '../../../services/app-context';
import {
getTriggerVotes,
- labelCompare,
showNewSubmitRequirements,
+ computeLabels,
} from '../../../utils/label-util';
import {ChangeStatus} from '../../../constants/constants';
import {fontStyles} from '../../../styles/gr-font-styles';
@@ -106,7 +105,7 @@
}
private renderOldSubmitRequirements() {
- const labels = this._computeLabels();
+ const labels = computeLabels(this.account, this.change);
return html`${this.renderLabels(labels)}${this.renderErrorMessages()}`;
}
@@ -117,7 +116,7 @@
private renderSubmitReqsLabels() {
const triggerVotes = getTriggerVotes(this.change);
- const labels = this._computeLabels().filter(
+ const labels = computeLabels(this.account, this.change).filter(
label => !triggerVotes.includes(label.name)
);
if (!labels.length) return;
@@ -135,7 +134,7 @@
private renderTriggerVotes() {
const triggerVotes = getTriggerVotes(this.change);
- const labels = this._computeLabels().filter(label =>
+ const labels = computeLabels(this.account, this.change).filter(label =>
triggerVotes.includes(label.name)
);
if (!labels.length) return;
@@ -214,27 +213,6 @@
return labels;
}
- private getStringLabelValue(
- labels: LabelNameToInfoMap,
- labelName: string,
- numberValue?: number
- ): string {
- const detailedInfo = labels[labelName] as DetailedLabelInfo;
- if (detailedInfo.values) {
- for (const labelValue of Object.keys(detailedInfo.values)) {
- if (Number(labelValue) === numberValue) {
- return labelValue;
- }
- }
- }
- // TODO: This code is sometimes executed with numberValue taking the
- // values 0 and undefined.
- // For now it is unclear how this is happening, ideally this code should
- // never be executed.
- const stringVal = `${numberValue}`;
- return stringVal;
- }
-
private getDefaultValue(labelName?: string) {
const labels = this.change?.labels;
if (!labelName || !labels?.[labelName]) return undefined;
@@ -242,43 +220,6 @@
return labelInfo.default_value;
}
- _getVoteForAccount(labelName: string): string | null {
- const labels = this.change?.labels;
- if (!labels) return null;
- const votes = labels[labelName] as DetailedLabelInfo;
- if (votes.all && votes.all.length > 0) {
- for (let i = 0; i < votes.all.length; i++) {
- if (
- this.account &&
- // TODO(TS): Replace == with === and check code can assign string to _account_id instead of number
- // eslint-disable-next-line eqeqeq
- votes.all[i]._account_id == this.account._account_id
- ) {
- return this.getStringLabelValue(
- labels,
- labelName,
- votes.all[i].value
- );
- }
- }
- }
- return null;
- }
-
- _computeLabels(): Label[] {
- if (!this.account) return [];
- const labelsObj = this.change?.labels;
- if (!labelsObj) return [];
- return Object.keys(labelsObj)
- .sort(labelCompare)
- .map(key => {
- return {
- name: key,
- value: this._getVoteForAccount(key),
- };
- });
- }
-
_computeColumns() {
if (!this.permittedLabels) return;
const labels = Object.keys(this.permittedLabels);
diff --git a/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_test.ts b/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_test.ts
index 0310424..5b743e2 100644
--- a/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_test.ts
+++ b/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_test.ts
@@ -26,6 +26,7 @@
createChange,
} from '../../../test/test-data-generators';
import {ChangeStatus} from '../../../constants/constants';
+import {getVoteForAccount} from '../../../utils/label-util';
const basicFixture = fixtureFromElement('gr-label-scores');
@@ -118,7 +119,10 @@
test('_getVoteForAccount', () => {
const labelName = 'Code-Review';
- assert.strictEqual(element._getVoteForAccount(labelName), '+1');
+ assert.strictEqual(
+ getVoteForAccount(labelName, element.account, element.change),
+ '+1'
+ );
});
test('_computeColumns', () => {
@@ -132,61 +136,6 @@
});
});
- test('changes in label score are reflected in _labels', async () => {
- const change = {
- ...createChange(),
- labels: {
- 'Code-Review': {
- values: {
- '0': 'No score',
- '+1': 'good',
- '+2': 'excellent',
- '-1': 'bad',
- '-2': 'terrible',
- },
- default_value: 0,
- },
- Verified: {
- values: {
- '0': 'No score',
- '+1': 'good',
- '+2': 'excellent',
- '-1': 'bad',
- '-2': 'terrible',
- },
- default_value: 0,
- },
- },
- };
- element.change = change;
- await flush();
- let labels = element._computeLabels();
- assert.deepEqual(labels, [
- {name: 'Code-Review', value: null},
- {name: 'Verified', value: null},
- ]);
- element.change = {
- ...change,
- labels: {
- ...change.labels,
- Verified: {
- ...change.labels.Verified,
- all: [
- {
- _account_id: accountId,
- value: 1,
- },
- ],
- },
- },
- };
- await flush();
- labels = element._computeLabels();
- assert.deepEqual(labels, [
- {name: 'Code-Review', value: null},
- {name: 'Verified', value: '+1'},
- ]);
- });
suite('message', () => {
test('shown when change is abandoned', async () => {
element.change = {
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 b6b3a61..28de23c 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
@@ -197,14 +197,17 @@
closeShown: this.removable,
})}"
>
- <gr-account-label
- .account="${this.account}"
- .change="${this.change}"
- ?forceAttention=${this.forceAttention}
- ?highlightAttention=${this.highlightAttention}
- .voteableText=${this.voteableText}
- >
- </gr-account-label>
+ <div>
+ <gr-account-label
+ .account="${this.account}"
+ .change="${this.change}"
+ ?forceAttention=${this.forceAttention}
+ ?highlightAttention=${this.highlightAttention}
+ .voteableText=${this.voteableText}
+ clickable
+ >
+ </gr-account-label>
+ </div>
<slot name="vote-chip"></slot>
<gr-button
id="remove"
diff --git a/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip_test.ts b/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip_test.ts
index af0ea9b..0379c8a 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip_test.ts
+++ b/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip_test.ts
@@ -39,7 +39,9 @@
test('renders', () => {
expect(element).shadowDom.to.equal(/* HTML */ `
<div class="container">
- <gr-account-label deselected=""></gr-account-label>
+ <div>
+ <gr-account-label clickable="" deselected=""></gr-account-label>
+ </div>
<slot name="vote-chip"></slot>
<gr-button
aria-disabled="false"
diff --git a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.ts b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.ts
index 0c85c20..d9a919e 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.ts
+++ b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.ts
@@ -27,11 +27,12 @@
import {fireEvent} from '../../../utils/event-util';
import {isInvolved} from '../../../utils/change-util';
import {ShowAlertEventDetail} from '../../../types/events';
-import {LitElement, css, html} from 'lit';
+import {LitElement, css, html, TemplateResult} from 'lit';
import {customElement, property, state} from 'lit/decorators';
import {classMap} from 'lit/directives/class-map';
import {getRemovedByIconClickReason} from '../../../utils/attention-set-util';
import {ifDefined} from 'lit/directives/if-defined';
+import {GerritNav} from '../../core/gr-navigation/gr-navigation';
@customElement('gr-account-label')
export class GrAccountLabel extends LitElement {
@@ -98,6 +99,9 @@
deselected = false;
@property({type: Boolean, reflect: true})
+ clickable = false;
+
+ @property({type: Boolean, reflect: true})
attentionIconShown = false;
@property({type: Boolean, reflect: true})
@@ -174,7 +178,6 @@
}
.name {
display: inline-block;
- text-decoration: inherit;
vertical-align: top;
overflow: hidden;
text-overflow: ellipsis;
@@ -183,6 +186,13 @@
.hasAttention .name {
font-weight: var(--font-weight-bold);
}
+ a.ownerLink {
+ text-decoration: none;
+ color: var(--primary-text-color);
+ }
+ :host([clickable]) a.ownerLink:hover .name {
+ text-decoration: underline;
+ }
`,
];
}
@@ -245,26 +255,31 @@
</gr-button>
</gr-tooltip-content>`
: ''}
- <span
- class="${classMap({
- hovercardTargetWrapper: true,
- hasAttention: this.attentionIconShown,
- })}"
- >
- ${this.avatarShown
- ? html`<gr-avatar .account="${account}" imageSize="32"></gr-avatar>`
- : ''}
+ ${this.maybeRenderLink(html`
<span
- tabindex=${this.hideHovercard ? '-1' : '0'}
- role=${ifDefined(this.hideHovercard ? undefined : 'button')}
- id="hovercardTarget"
- class="name"
- part="gr-account-label-text"
+ class="${classMap({
+ hovercardTargetWrapper: true,
+ hasAttention: this.attentionIconShown,
+ })}"
>
- ${this._computeName(account, this.firstName, this._config)}
+ ${this.avatarShown
+ ? html`<gr-avatar
+ .account="${account}"
+ imageSize="32"
+ ></gr-avatar>`
+ : ''}
+ <span
+ tabindex=${this.hideHovercard ? '-1' : '0'}
+ role=${ifDefined(this.hideHovercard ? undefined : 'button')}
+ id="hovercardTarget"
+ class="name"
+ part="gr-account-label-text"
+ >
+ ${this._computeName(account, this.firstName, this._config)}
+ </span>
+ ${this.renderAccountStatusPlugins()}
</span>
- ${this.renderAccountStatusPlugins()}
- </span>
+ `)}
</div>
`;
}
@@ -283,6 +298,18 @@
});
}
+ private maybeRenderLink(span: TemplateResult) {
+ if (!this.clickable || !this.account) return span;
+ const url = GerritNav.getUrlForOwner(
+ this.account.email ||
+ this.account.username ||
+ this.account.name ||
+ `${this.account._account_id}`
+ );
+ if (!url) return span;
+ return html`<a class="ownerLink" href="${url}" tabindex="-1">${span}</a>`;
+ }
+
private renderAccountStatusPlugins() {
if (!this.account?._account_id || this.noStatusIcons) {
return;
diff --git a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label_test.ts b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label_test.ts
index 5d8ef6e..cad814cd 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label_test.ts
+++ b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label_test.ts
@@ -24,9 +24,10 @@
stubRestApi,
} from '../../../test/test-utils';
import {GrAccountLabel} from './gr-account-label';
+import {GerritNav} from '../../core/gr-navigation/gr-navigation';
import {AccountDetailInfo, ServerInfo} from '../../../types/common';
import {
- createAccountDetailWithId,
+ createAccountDetailWithIdNameAndEmail,
createChange,
createPluginConfig,
createServerInfo,
@@ -38,11 +39,12 @@
suite('gr-account-label tests', () => {
let element: GrAccountLabel;
const kermit: AccountDetailInfo = {
- ...createAccountDetailWithId(31),
+ ...createAccountDetailWithIdNameAndEmail(31),
name: 'kermit',
};
setup(async () => {
+ sinon.stub(GerritNav, 'getUrlForOwner').callsFake(() => 'test');
stubRestApi('getAccount').resolves(kermit);
stubRestApi('getLoggedIn').resolves(false);
stubRestApi('getConfig').resolves({
@@ -88,6 +90,38 @@
`);
});
+ test('renders clickable', async () => {
+ element.account = kermit;
+ element.clickable = true;
+ await element.updateComplete;
+ expect(element).shadowDom.to.equal(/* HTML */ `
+ <div class="container">
+ <gr-hovercard-account for="hovercardTarget"></gr-hovercard-account>
+ <a class="ownerLink" href="test" tabindex="-1">
+ <span class="hovercardTargetWrapper">
+ <gr-avatar hidden="" imagesize="32"> </gr-avatar>
+ <span
+ class="name"
+ id="hovercardTarget"
+ part="gr-account-label-text"
+ role="button"
+ tabindex="0"
+ >
+ kermit
+ </span>
+ <gr-endpoint-decorator
+ class="accountStatusDecorator"
+ name="account-status-icon"
+ >
+ <gr-endpoint-param name="accountId"></gr-endpoint-param>
+ <span class="rightSidePadding"></span>
+ </gr-endpoint-decorator>
+ </span>
+ </a>
+ </div>
+ `);
+ });
+
suite('_computeName', () => {
test('not showing anonymous', () => {
const account = {name: 'Wyatt'};
@@ -137,14 +171,14 @@
};
element._selfAccount = kermit;
element.account = {
- ...createAccountDetailWithId(42),
+ ...createAccountDetailWithIdNameAndEmail(42),
name: 'ernie',
};
element.change = {
...createChange(),
attention_set: {
42: {
- account: createAccountDetailWithId(42),
+ account: createAccountDetailWithIdNameAndEmail(42),
},
},
owner: kermit,
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 cdbdcab..36a7354 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
@@ -300,6 +300,7 @@
</td>
<td>
<gr-account-label
+ clickable
.account="${mappedLabel.account}"
.change="${change}"
></gr-account-label>
diff --git a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder.ts b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder.ts
index cb1145c..2a80fdf 100644
--- a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder.ts
+++ b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder.ts
@@ -198,13 +198,17 @@
startLine: LineNumber,
endLine: LineNumber,
side: Side
- ) {
+ ): GrDiffGroup[] {
const startIndex = this.groups.findIndex(group =>
group.containsLine(side, startLine)
);
- const endIndex = this.groups.findIndex(group =>
+ if (startIndex === -1) return [];
+ let endIndex = this.groups.findIndex(group =>
group.containsLine(side, endLine)
);
+ // Not all groups may have rendered yet. In that case let's just render
+ // *all* groups after `startIndex`.
+ if (endIndex === -1) endIndex = this.groups.length - 1;
// The filter preserves the legacy behavior to only return non-context
// groups
return this.groups
diff --git a/polygerrit-ui/app/embed/diff/gr-syntax-layer/gr-syntax-layer-worker.ts b/polygerrit-ui/app/embed/diff/gr-syntax-layer/gr-syntax-layer-worker.ts
index aa2ad07..3c6a498 100644
--- a/polygerrit-ui/app/embed/diff/gr-syntax-layer/gr-syntax-layer-worker.ts
+++ b/polygerrit-ui/app/embed/diff/gr-syntax-layer/gr-syntax-layer-worker.ts
@@ -122,8 +122,10 @@
'template-tag',
'template-variable',
'title',
+ 'title function_',
'type',
'variable',
+ 'variable language_',
]);
export class GrSyntaxLayerWorker implements DiffLayer {
diff --git a/polygerrit-ui/app/embed/diff/gr-syntax-themes/gr-syntax-theme.ts b/polygerrit-ui/app/embed/diff/gr-syntax-themes/gr-syntax-theme.ts
index 267aaaf..b74cbb3 100644
--- a/polygerrit-ui/app/embed/diff/gr-syntax-themes/gr-syntax-theme.ts
+++ b/polygerrit-ui/app/embed/diff/gr-syntax-themes/gr-syntax-theme.ts
@@ -52,6 +52,9 @@
.gr-syntax-variable {
color: var(--syntax-variable-color);
}
+ .gr-syntax-variable.language_ {
+ color: var(--syntax-variable-language-color);
+ }
.gr-syntax-template-variable {
color: var(--syntax-template-variable-color);
}
@@ -82,6 +85,9 @@
.gr-syntax-title {
color: var(--syntax-title-color);
}
+ .gr-syntax-title.function_ {
+ color: var(--syntax-title-function-color);
+ }
.gr-syntax-attr {
color: var(--syntax-attr-color);
}
diff --git a/polygerrit-ui/app/styles/themes/app-theme.ts b/polygerrit-ui/app/styles/themes/app-theme.ts
index 4e719b7..f31af21 100644
--- a/polygerrit-ui/app/styles/themes/app-theme.ts
+++ b/polygerrit-ui/app/styles/themes/app-theme.ts
@@ -409,19 +409,21 @@
--syntax-meta-keyword-color: #219;
--syntax-number-color: #164;
--syntax-params-color: var(--primary-text-color);
+ --syntax-property-color: var(--primary-text-color);
--syntax-regexp-color: #fa8602;
--syntax-selector-attr-color: #fa8602;
--syntax-selector-class-color: #164;
--syntax-selector-id-color: #2a00ff;
- --syntax-property-color: #fa8602;
--syntax-selector-pseudo-color: #fa8602;
--syntax-string-color: #2a00ff;
--syntax-tag-color: #170;
--syntax-template-tag-color: #fa8602;
--syntax-template-variable-color: #0000c0;
--syntax-title-color: #0000c0;
+ --syntax-title-function-color: var(--syntax-title-color);
--syntax-type-color: var(--blue-700);
--syntax-variable-color: var(--primary-text-color);
+ --syntax-variable-language-color: var(--syntax-built_in-color);
/* elevation */
--elevation-level-1: 0px 1px 2px 0px rgba(60, 64, 67, 0.3),
diff --git a/polygerrit-ui/app/styles/themes/dark-theme.ts b/polygerrit-ui/app/styles/themes/dark-theme.ts
index c8f0d47..6856df8 100644
--- a/polygerrit-ui/app/styles/themes/dark-theme.ts
+++ b/polygerrit-ui/app/styles/themes/dark-theme.ts
@@ -246,19 +246,21 @@
--syntax-meta-keyword-color: #eefff7;
--syntax-number-color: #00998a;
--syntax-params-color: var(--primary-text-color);
+ --syntax-property-color: #c792ea;
--syntax-regexp-color: #f77669;
--syntax-selector-attr-color: #80cbbf;
--syntax-selector-class-color: #ffcb68;
--syntax-selector-id-color: #f77669;
--syntax-selector-pseudo-color: #c792ea;
- --syntax-property-color: #c792ea;
--syntax-string-color: #c3e88d;
--syntax-tag-color: #f77669;
--syntax-template-tag-color: #c792ea;
--syntax-template-variable-color: #f77669;
--syntax-title-color: #75a5ff;
+ --syntax-title-function-color: var(--syntax-title-color);
--syntax-type-color: #dd5f5f;
--syntax-variable-color: #f77669;
+ --syntax-variable-language-color: var(--syntax-built_in-color);
/* misc */
--line-length-indicator-color: #d7aefb;
diff --git a/polygerrit-ui/app/test/test-data-generators.ts b/polygerrit-ui/app/test/test-data-generators.ts
index 329ea11..9666387 100644
--- a/polygerrit-ui/app/test/test-data-generators.ts
+++ b/polygerrit-ui/app/test/test-data-generators.ts
@@ -201,6 +201,17 @@
};
}
+export function createAccountDetailWithIdNameAndEmail(
+ id = 5
+): AccountDetailInfo {
+ return {
+ _account_id: id as AccountId,
+ email: `user-${id}@` as EmailAddress,
+ name: `User-${id}`,
+ registered_on: dateToTimestamp(new Date(2020, 10, 15, 14, 5, 8)),
+ };
+}
+
export function createReviewers(): Reviewers {
return {};
}
diff --git a/polygerrit-ui/app/utils/label-util.ts b/polygerrit-ui/app/utils/label-util.ts
index 563e864..aa55ea2 100644
--- a/polygerrit-ui/app/utils/label-util.ts
+++ b/polygerrit-ui/app/utils/label-util.ts
@@ -32,6 +32,7 @@
} from '../types/common';
import {ParsedChangeInfo} from '../types/types';
import {assertNever, unique} from './common-util';
+import {Label} from '../elements/change/gr-label-score-row/gr-label-score-row';
// Name of the standard Code-Review label.
export enum StandardLabels {
@@ -294,6 +295,60 @@
return priorityRequirementList.concat(nonPriorityRequirements);
}
+function getStringLabelValue(
+ labels: LabelNameToInfoMap,
+ labelName: string,
+ numberValue?: number
+): string {
+ const detailedInfo = labels[labelName] as DetailedLabelInfo;
+ if (detailedInfo.values) {
+ for (const labelValue of Object.keys(detailedInfo.values)) {
+ if (Number(labelValue) === numberValue) {
+ return labelValue;
+ }
+ }
+ }
+ // TODO: This code is sometimes executed with numberValue taking the
+ // values 0 and undefined.
+ // For now it is unclear how this is happening, ideally this code should
+ // never be executed.
+ return `${numberValue}`;
+}
+
+export function getVoteForAccount(
+ labelName: string,
+ account?: AccountInfo,
+ change?: ChangeInfo
+): string | null {
+ const labels = change?.labels;
+ if (!account || !labels) return null;
+ const votes = labels[labelName] as DetailedLabelInfo;
+ if (!votes.all?.length) return null;
+ for (let i = 0; i < votes.all.length; i++) {
+ if (votes.all[i]._account_id === account._account_id) {
+ return getStringLabelValue(labels, labelName, votes.all[i].value);
+ }
+ }
+ return null;
+}
+
+export function computeLabels(
+ account?: AccountInfo,
+ change?: ChangeInfo
+): Label[] {
+ if (!account) return [];
+ const labelsObj = change?.labels;
+ if (!labelsObj) return [];
+ return Object.keys(labelsObj)
+ .sort(labelCompare)
+ .map(key => {
+ return {
+ name: key,
+ value: getVoteForAccount(key, account, change),
+ };
+ });
+}
+
export function getTriggerVotes(change?: ParsedChangeInfo | ChangeInfo) {
const allLabels = Object.keys(change?.labels ?? {});
// Normally there is utility method getRequirements, which filter out
diff --git a/polygerrit-ui/app/utils/label-util_test.ts b/polygerrit-ui/app/utils/label-util_test.ts
index f4e1da9..56fb58c 100644
--- a/polygerrit-ui/app/utils/label-util_test.ts
+++ b/polygerrit-ui/app/utils/label-util_test.ts
@@ -29,6 +29,7 @@
hasNeutralStatus,
labelCompare,
LabelStatus,
+ computeLabels,
} from './label-util';
import {
AccountId,
@@ -45,10 +46,12 @@
createSubmitRequirementResultInfo,
createNonApplicableSubmitRequirementResultInfo,
createDetailedLabelInfo,
+ createAccountWithId,
} from '../test/test-data-generators';
import {
SubmitRequirementResultInfo,
SubmitRequirementStatus,
+ LabelNameToInfoMap,
} from '../api/rest-api';
const VALUES_0 = {
@@ -237,6 +240,58 @@
assert.equal(getRepresentativeValue(labelInfo), -2);
});
+ test('computeLabels', async () => {
+ const accountId = 123 as AccountId;
+ const account = createAccountWithId(accountId);
+ const change = {
+ ...createChange(),
+ labels: {
+ 'Code-Review': {
+ values: {
+ '0': 'No score',
+ '+1': 'good',
+ '+2': 'excellent',
+ '-1': 'bad',
+ '-2': 'terrible',
+ },
+ default_value: 0,
+ } as DetailedLabelInfo,
+ Verified: {
+ values: {
+ '0': 'No score',
+ '+1': 'good',
+ '+2': 'excellent',
+ '-1': 'bad',
+ '-2': 'terrible',
+ },
+ default_value: 0,
+ } as DetailedLabelInfo,
+ } as LabelNameToInfoMap,
+ };
+ let labels = computeLabels(account, change);
+ assert.deepEqual(labels, [
+ {name: 'Code-Review', value: null},
+ {name: 'Verified', value: null},
+ ]);
+ change.labels = {
+ ...change.labels,
+ Verified: {
+ ...change.labels.Verified,
+ all: [
+ {
+ _account_id: accountId,
+ value: 1,
+ },
+ ],
+ } as DetailedLabelInfo,
+ } as LabelNameToInfoMap;
+ labels = computeLabels(account, change);
+ assert.deepEqual(labels, [
+ {name: 'Code-Review', value: null},
+ {name: 'Verified', value: '+1'},
+ ]);
+ });
+
suite('extractAssociatedLabels()', () => {
function createSubmitRequirementExpressionInfoWith(expression: string) {
return {