Merge "Submit Requirements - Do not show SR hovercard in dashboard"
diff --git a/java/com/google/gerrit/httpd/raw/StaticModule.java b/java/com/google/gerrit/httpd/raw/StaticModule.java
index aa32169..460ad60 100644
--- a/java/com/google/gerrit/httpd/raw/StaticModule.java
+++ b/java/com/google/gerrit/httpd/raw/StaticModule.java
@@ -79,6 +79,7 @@
"/dashboard/*",
"/groups/self",
"/settings/*",
+ "/topic/*",
"/Documentation/q/*");
/**
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 43f6730..8ffc0aa 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
@@ -47,9 +47,12 @@
QuickLabelInfo,
Timestamp,
} from '../../../types/common';
-import {hasOwnProperty} from '../../../utils/common-util';
+import {assertNever, hasOwnProperty} from '../../../utils/common-util';
import {pluralize} from '../../../utils/string-util';
import {ChangeStates} from '../../shared/gr-change-status/gr-change-status';
+import {KnownExperimentId} from '../../../services/flags/flags';
+import {getRequirements, iconForStatus} from '../../../utils/label-util';
+import {SubmitRequirementStatus} from '../../../api/rest-api';
enum ChangeSize {
XS = 10,
@@ -121,8 +124,20 @@
@property({type: Array})
_dynamicCellEndpoints?: string[];
+ @property({type: Boolean})
+ _isSubmitRequirementsUiEnabled = false;
+
reporting: ReportingService = appContext.reportingService;
+ private readonly flagsService = appContext.flagsService;
+
+ override ready() {
+ super.ready();
+ this._isSubmitRequirementsUiEnabled = this.flagsService.isEnabled(
+ KnownExperimentId.SUBMIT_REQUIREMENTS_UI
+ );
+ }
+
override connectedCallback() {
super.connectedCallback();
getPluginLoader()
@@ -167,8 +182,33 @@
}
_computeLabelClass(change: ChangeInfo | undefined, labelName: string) {
- const category = this._computeLabelCategory(change, labelName);
const classes = ['cell', 'label'];
+ if (this._isSubmitRequirementsUiEnabled) {
+ const requirements = getRequirements(change).filter(
+ sr => sr.name === labelName
+ );
+ if (requirements.length === 1) {
+ const status = requirements[0].status;
+ switch (status) {
+ case SubmitRequirementStatus.SATISFIED:
+ classes.push('u-green');
+ break;
+ case SubmitRequirementStatus.UNSATISFIED:
+ classes.push('u-red');
+ break;
+ case SubmitRequirementStatus.OVERRIDDEN:
+ classes.push('u-green');
+ break;
+ case SubmitRequirementStatus.NOT_APPLICABLE:
+ classes.push('u-gray-background');
+ break;
+ default:
+ assertNever(status, `Unsupported status: ${status}`);
+ }
+ return classes.sort().join(' ');
+ }
+ }
+ const category = this._computeLabelCategory(change, labelName);
switch (category) {
case LabelCategory.NOT_APPLICABLE:
classes.push('u-gray-background');
@@ -196,6 +236,14 @@
}
_computeLabelIcon(change: ChangeInfo | undefined, labelName: string): string {
+ if (this._isSubmitRequirementsUiEnabled) {
+ const requirements = getRequirements(change).filter(
+ sr => sr.name === labelName
+ );
+ if (requirements.length === 1) {
+ return `gr-icons:${iconForStatus(requirements[0].status)}`;
+ }
+ }
const category = this._computeLabelCategory(change, labelName);
switch (category) {
case LabelCategory.APPROVED:
diff --git a/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display.ts b/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display.ts
index 1ae0992..2a3936f 100644
--- a/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display.ts
+++ b/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display.ts
@@ -25,6 +25,9 @@
@customElement('gr-key-binding-display')
export class GrKeyBindingDisplay extends LitElement {
+ @property({type: Array})
+ binding: string[][] = [];
+
static override get styles() {
return [
css`
@@ -53,9 +56,6 @@
return html`${items}`;
}
- @property({type: Array})
- binding: string[][] = [];
-
_computeModifiers(binding: string[]) {
return binding.slice(0, binding.length - 1);
}
diff --git a/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.ts b/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.ts
index 8610999..2e51e64 100644
--- a/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.ts
+++ b/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.ts
@@ -16,15 +16,14 @@
*/
import '../../shared/gr-button/gr-button';
import '../gr-key-binding-display/gr-key-binding-display';
-import '../../../styles/shared-styles';
-import '../../../styles/gr-font-styles';
-import {PolymerElement} from '@polymer/polymer/polymer-element';
-import {htmlTemplate} from './gr-keyboard-shortcuts-dialog_html';
+import {sharedStyles} from '../../../styles/shared-styles';
+import {fontStyles} from '../../../styles/gr-font-styles';
+import {LitElement, css, html} from 'lit';
+import {customElement, property} from 'lit/decorators';
import {
ShortcutSection,
SectionView,
} from '../../../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin';
-import {property, customElement} from '@polymer/decorators';
import {appContext} from '../../../services/app-context';
import {ShortcutViewListener} from '../../../services/shortcuts/shortcuts-service';
@@ -40,11 +39,7 @@
}
@customElement('gr-keyboard-shortcuts-dialog')
-export class GrKeyboardShortcutsDialog extends PolymerElement {
- static get template() {
- return htmlTemplate;
- }
-
+export class GrKeyboardShortcutsDialog extends LitElement {
/**
* Fired when the user presses the close button.
*
@@ -67,9 +62,107 @@
this._onDirectoryUpdated(d);
}
- override ready() {
- super.ready();
- this._ensureAttribute('role', 'dialog');
+ static override get styles() {
+ return [
+ sharedStyles,
+ fontStyles,
+ css`
+ :host {
+ display: block;
+ max-height: 100vh;
+ overflow-y: auto;
+ }
+ header {
+ padding: var(--spacing-l);
+ }
+ main {
+ display: flex;
+ padding: 0 var(--spacing-xxl) var(--spacing-xxl);
+ }
+ .column {
+ flex: 50%;
+ }
+ header {
+ align-items: center;
+ border-bottom: 1px solid var(--border-color);
+ display: flex;
+ justify-content: space-between;
+ }
+ table caption {
+ font-weight: var(--font-weight-bold);
+ padding-top: var(--spacing-l);
+ text-align: left;
+ }
+ tr {
+ height: 32px;
+ }
+ td {
+ padding: var(--spacing-xs) 0;
+ }
+ td:first-child,
+ th:first-child {
+ padding-right: var(--spacing-m);
+ text-align: right;
+ width: 160px;
+ color: var(--deemphasized-text-color);
+ }
+ td:second-child {
+ min-width: 200px;
+ }
+ th {
+ color: var(--deemphasized-text-color);
+ text-align: left;
+ }
+ .header {
+ font-weight: var(--font-weight-bold);
+ padding-top: var(--spacing-l);
+ }
+ .modifier {
+ font-weight: var(--font-weight-normal);
+ }
+ `,
+ ];
+ }
+
+ override render() {
+ return html`<header>
+ <h3 class="heading-3">Keyboard shortcuts</h3>
+ <gr-button link="" @click=${this.handleCloseTap}>Close</gr-button>
+ </header>
+ <main>
+ <div class="column">
+ ${this._left?.map(section => this.renderSection(section))}
+ </div>
+ <div class="column">
+ ${this._right?.map(section => this.renderSection(section))}
+ </div>
+ </main>
+ <footer></footer>`;
+ }
+
+ private renderSection(section: SectionShortcut) {
+ return html`<table>
+ <caption>
+ ${section.section}
+ </caption>
+ <thead>
+ <tr>
+ <th>Key</th>
+ <th>Action</th>
+ </tr>
+ </thead>
+ <tbody>
+ ${section.shortcuts?.map(
+ shortcut => html`<tr>
+ <td>
+ <gr-key-binding-display .binding=${shortcut.binding}>
+ </gr-key-binding-display>
+ </td>
+ <td>${shortcut.text}</td>
+ </tr>`
+ )}
+ </tbody>
+ </table>`;
}
override connectedCallback() {
@@ -82,7 +175,7 @@
super.disconnectedCallback();
}
- _handleCloseTap(e: MouseEvent) {
+ private handleCloseTap(e: MouseEvent) {
e.preventDefault();
e.stopPropagation();
this.dispatchEvent(
@@ -142,7 +235,7 @@
});
}
- this.set('_left', left);
- this.set('_right', right);
+ this._right = right;
+ this._left = left;
}
}
diff --git a/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog_html.ts b/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog_html.ts
deleted file mode 100644
index 4992daa..0000000
--- a/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog_html.ts
+++ /dev/null
@@ -1,137 +0,0 @@
-/**
- * @license
- * Copyright (C) 2020 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 {html} from '@polymer/polymer/lib/utils/html-tag';
-
-export const htmlTemplate = html`
- <style include="gr-font-styles">
- /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
- </style>
- <style include="shared-styles">
- :host {
- display: block;
- max-height: 100vh;
- overflow-y: auto;
- }
- header {
- padding: var(--spacing-l);
- }
- main {
- display: flex;
- padding: 0 var(--spacing-xxl) var(--spacing-xxl);
- }
- .column {
- flex: 50%;
- }
- header {
- align-items: center;
- border-bottom: 1px solid var(--border-color);
- display: flex;
- justify-content: space-between;
- }
- table caption {
- font-weight: var(--font-weight-bold);
- padding-top: var(--spacing-l);
- text-align: left;
- }
- tr {
- height: 32px;
- }
- td {
- padding: var(--spacing-xs) 0;
- }
- td:first-child,
- th:first-child {
- padding-right: var(--spacing-m);
- text-align: right;
- width: 160px;
- color: var(--deemphasized-text-color);
- }
- td:second-child {
- min-width: 200px;
- }
- th {
- color: var(--deemphasized-text-color);
- text-align: left;
- }
- .header {
- font-weight: var(--font-weight-bold);
- padding-top: var(--spacing-l);
- }
- .modifier {
- font-weight: var(--font-weight-normal);
- }
- </style>
- <header>
- <h3 class="heading-3">Keyboard shortcuts</h3>
- <gr-button link="" on-click="_handleCloseTap">Close</gr-button>
- </header>
- <main>
- <div class="column">
- <template is="dom-repeat" items="[[_left]]">
- <table>
- <caption>
- [[item.section]]
- </caption>
- <thead>
- <tr>
- <th>Key</th>
- <th>Action</th>
- </tr>
- </thead>
- <tbody>
- <template is="dom-repeat" items="[[item.shortcuts]]" as="shortcut">
- <tr>
- <td>
- <gr-key-binding-display binding="[[shortcut.binding]]">
- </gr-key-binding-display>
- </td>
- <td>[[shortcut.text]]</td>
- </tr>
- </template>
- </tbody>
- </table>
- </template>
- </div>
- <div class="column">
- <template is="dom-repeat" items="[[_right]]">
- <table>
- <caption>
- [[item.section]]
- </caption>
- <thead>
- <tr>
- <th>Key</th>
- <th>Action</th>
- </tr>
- </thead>
- <tbody>
- <template is="dom-repeat" items="[[item.shortcuts]]" as="shortcut">
- <tr>
- <td>
- <gr-key-binding-display binding="[[shortcut.binding]]">
- </gr-key-binding-display>
- </td>
- <td>[[shortcut.text]]</td>
- </tr>
- </template>
- </tbody>
- </table>
- </template>
- </div>
- </main>
- <footer></footer>
-`;
diff --git a/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog_test.ts b/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog_test.ts
index 2c76704..7fc52f5 100644
--- a/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog_test.ts
+++ b/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog_test.ts
@@ -16,6 +16,7 @@
*/
import '../../../test/common-test-setup-karma';
+import './gr-keyboard-shortcuts-dialog';
import {GrKeyboardShortcutsDialog} from './gr-keyboard-shortcuts-dialog';
import {
SectionView,
@@ -27,8 +28,9 @@
suite('gr-keyboard-shortcuts-dialog tests', () => {
let element: GrKeyboardShortcutsDialog;
- setup(() => {
+ setup(async () => {
element = basicFixture.instantiate();
+ await flush();
});
function update(directory: Map<ShortcutSection, SectionView>) {
diff --git a/polygerrit-ui/app/services/comments/comments-service.ts b/polygerrit-ui/app/services/comments/comments-service.ts
index 5896b52..b5745a8 100644
--- a/polygerrit-ui/app/services/comments/comments-service.ts
+++ b/polygerrit-ui/app/services/comments/comments-service.ts
@@ -39,24 +39,45 @@
export class CommentsService {
private discardedDrafts?: UIDraft[] = [];
+ private changeNum?: NumericChangeId;
+
+ private patchNum?: PatchSetNum;
+
constructor(readonly restApiService: RestApiService) {
discardedDrafts$.subscribe(
discardedDrafts => (this.discardedDrafts = discardedDrafts)
);
changeNum$.subscribe(changeNum => {
+ this.changeNum = changeNum;
updateStateReset();
- if (!changeNum) return;
- this.reloadComments(changeNum);
- this.reloadRobotComments(changeNum);
- this.reloadDrafts(changeNum);
+ this.reloadAllComments();
});
combineLatest([changeNum$, currentPatchNum$]).subscribe(
- ([changeNum, currentPatchNum]) => {
- if (!changeNum || !currentPatchNum) return;
- this.reloadPortedComments(changeNum, currentPatchNum);
- this.reloadPortedDrafts(changeNum, currentPatchNum);
+ ([changeNum, patchNum]) => {
+ this.changeNum = changeNum;
+ this.patchNum = patchNum;
+ this.reloadAllPortedComments();
}
);
+ document.addEventListener('reload', () => {
+ this.reloadAllComments();
+ this.reloadAllPortedComments();
+ });
+ }
+
+ // Note that this does *not* reload ported comments.
+ reloadAllComments() {
+ if (!this.changeNum) return;
+ this.reloadComments(this.changeNum);
+ this.reloadRobotComments(this.changeNum);
+ this.reloadDrafts(this.changeNum);
+ }
+
+ reloadAllPortedComments() {
+ if (!this.changeNum) return;
+ if (!this.patchNum) return;
+ this.reloadPortedComments(this.changeNum, this.patchNum);
+ this.reloadPortedDrafts(this.changeNum, this.patchNum);
}
reloadComments(changeNum: NumericChangeId): Promise<void> {
diff --git a/polygerrit-ui/app/services/shortcuts/shortcuts-config.ts b/polygerrit-ui/app/services/shortcuts/shortcuts-config.ts
index 3c9e058..18b321a 100644
--- a/polygerrit-ui/app/services/shortcuts/shortcuts-config.ts
+++ b/polygerrit-ui/app/services/shortcuts/shortcuts-config.ts
@@ -180,13 +180,13 @@
Shortcut.CURSOR_NEXT_CHANGE,
ShortcutSection.ACTIONS,
'Select next change',
- {key: 'j'}
+ {key: 'j', allowRepeat: true}
);
describe(
Shortcut.CURSOR_PREV_CHANGE,
ShortcutSection.ACTIONS,
'Select previous change',
- {key: 'k'}
+ {key: 'k', allowRepeat: true}
);
describe(
Shortcut.OPEN_CHANGE,
@@ -316,15 +316,15 @@
Shortcut.NEXT_LINE,
ShortcutSection.DIFFS,
'Go to next line',
- {key: 'j'},
- {key: Key.DOWN}
+ {key: 'j', allowRepeat: true},
+ {key: Key.DOWN, allowRepeat: true}
);
describe(
Shortcut.PREV_LINE,
ShortcutSection.DIFFS,
'Go to previous line',
- {key: 'k'},
- {key: Key.UP}
+ {key: 'k', allowRepeat: true},
+ {key: Key.UP, allowRepeat: true}
);
describe(
Shortcut.VISIBLE_LINE,
@@ -480,15 +480,15 @@
Shortcut.CURSOR_NEXT_FILE,
ShortcutSection.FILE_LIST,
'Select next file',
- {key: 'j'},
- {key: Key.DOWN}
+ {key: 'j', allowRepeat: true},
+ {key: Key.DOWN, allowRepeat: true}
);
describe(
Shortcut.CURSOR_PREV_FILE,
ShortcutSection.FILE_LIST,
'Select previous file',
- {key: 'k'},
- {key: Key.UP}
+ {key: 'k', allowRepeat: true},
+ {key: Key.UP, allowRepeat: true}
);
describe(
Shortcut.OPEN_FILE,
diff --git a/polygerrit-ui/app/services/shortcuts/shortcuts-service.ts b/polygerrit-ui/app/services/shortcuts/shortcuts-service.ts
index a26fa08..f2e9e98 100644
--- a/polygerrit-ui/app/services/shortcuts/shortcuts-service.ts
+++ b/polygerrit-ui/app/services/shortcuts/shortcuts-service.ts
@@ -137,7 +137,7 @@
listener: (e: KeyboardEvent) => void
) {
const wrappedListener = (e: KeyboardEvent) => {
- if (e.repeat) return;
+ if (e.repeat && !shortcut.allowRepeat) return;
if (!eventMatchesShortcut(e, shortcut)) return;
if (shortcut.combo) {
if (!this.isInSpecificComboKeyMode(shortcut.combo)) return;
diff --git a/polygerrit-ui/app/services/shortcuts/shortcuts-service_test.ts b/polygerrit-ui/app/services/shortcuts/shortcuts-service_test.ts
index 05c4f53..a024159 100644
--- a/polygerrit-ui/app/services/shortcuts/shortcuts-service_test.ts
+++ b/polygerrit-ui/app/services/shortcuts/shortcuts-service_test.ts
@@ -213,7 +213,10 @@
{
shortcut: Shortcut.NEXT_LINE,
text: 'Go to next line',
- bindings: [{key: 'j'}, {key: 'ArrowDown'}],
+ bindings: [
+ {allowRepeat: true, key: 'j'},
+ {allowRepeat: true, key: 'ArrowDown'},
+ ],
},
],
[ShortcutSection.NAVIGATION]: [
@@ -234,7 +237,10 @@
{
shortcut: Shortcut.NEXT_LINE,
text: 'Go to next line',
- bindings: [{key: 'j'}, {key: 'ArrowDown'}],
+ bindings: [
+ {allowRepeat: true, key: 'j'},
+ {allowRepeat: true, key: 'ArrowDown'},
+ ],
},
],
[ShortcutSection.EVERYWHERE]: [
diff --git a/polygerrit-ui/app/utils/dom-util.ts b/polygerrit-ui/app/utils/dom-util.ts
index e2fa8fe..bd0f742 100644
--- a/polygerrit-ui/app/utils/dom-util.ts
+++ b/polygerrit-ui/app/utils/dom-util.ts
@@ -338,6 +338,8 @@
combo?: ComboKey;
/** Defaults to no modifiers. */
modifiers?: Modifier[];
+ /** Defaults to false. If true, then `event.repeat === true` is allowed. */
+ allowRepeat?: boolean;
}
const ALPHA_NUM = new RegExp(/^[A-Za-z0-9]$/);
@@ -406,7 +408,7 @@
}
) {
const wrappedListener = (e: KeyboardEvent) => {
- if (e.repeat) return;
+ if (e.repeat && !shortcut.allowRepeat) return;
if (options.shouldSuppress && shouldSuppress(e)) return;
if (eventMatchesShortcut(e, shortcut)) {
listener(e);