Update the header of the Checks tab
Screenshot https://imgur.com/a/f4PX1Tt
Change-Id: I3d33f6a316b971135b3ce6fd79ae41ed413c707c
diff --git a/polygerrit-ui/app/elements/checks/gr-checks-results.ts b/polygerrit-ui/app/elements/checks/gr-checks-results.ts
index 197e11b..804d85f 100644
--- a/polygerrit-ui/app/elements/checks/gr-checks-results.ts
+++ b/polygerrit-ui/app/elements/checks/gr-checks-results.ts
@@ -26,21 +26,42 @@
} from 'lit-element';
import {GrLitElement} from '../lit/gr-lit-element';
import '@polymer/paper-tooltip/paper-tooltip';
-import {Category, Link, LinkIcon, RunStatus, Tag} from '../../api/checks';
+import {
+ Action,
+ Category,
+ Link,
+ LinkIcon,
+ RunStatus,
+ Tag,
+} from '../../api/checks';
import {sharedStyles} from '../../styles/shared-styles';
-import {CheckRun, RunResult} from '../../services/checks/checks-model';
+import {
+ allActions$,
+ checksPatchsetNumber$,
+ CheckRun,
+ RunResult,
+ someProvidersAreLoading$,
+} from '../../services/checks/checks-model';
import {
allResults,
+ fireActionTriggered,
hasCompletedWithoutResults,
hasResultsOf,
iconForCategory,
} from '../../services/checks/checks-util';
-import {assertIsDefined} from '../../utils/common-util';
+import {
+ assertIsDefined,
+ check,
+ checkRequiredProperty,
+} from '../../utils/common-util';
import {whenVisible} from '../../utils/dom-util';
import {durationString} from '../../utils/date-util';
import {charsOnly, pluralize} from '../../utils/string-util';
import {fireRunSelectionReset} from './gr-checks-util';
import {ChecksTabState} from '../../types/events';
+import {PatchSetNumber} from '../../types/common';
+import {latestPatchNum$} from '../../services/change/change-model';
+import {appContext} from '../../services/app-context';
@customElement('gr-result-row')
class GrResultRow extends GrLitElement {
@@ -330,8 +351,20 @@
runs: CheckRun[] = [];
@property()
+ actions: Action[] = [];
+
+ @property()
tabState?: ChecksTabState;
+ @property()
+ someProvidersAreLoading = false;
+
+ @property()
+ checksPatchsetNumber: PatchSetNumber | undefined = undefined;
+
+ @property()
+ latestPatchsetNumber: PatchSetNumber | undefined = undefined;
+
/**
* How many runs are selected in the runs panel?
* If 0, then the `runs` property contains all the runs there are.
@@ -354,13 +387,59 @@
*/
private isSectionExpandedByUser = new Map<Category | 'SUCCESS', boolean>();
+ private readonly checksService = appContext.checksService;
+
+ constructor() {
+ super();
+ this.subscribe('actions', allActions$);
+ this.subscribe('checksPatchsetNumber', checksPatchsetNumber$);
+ this.subscribe('latestPatchsetNumber', latestPatchNum$);
+ this.subscribe('someProvidersAreLoading', someProvidersAreLoading$);
+ }
+
static get styles() {
return [
sharedStyles,
css`
:host {
display: block;
- padding: var(--spacing-xl);
+ background-color: var(--background-color-secondary);
+ }
+ .header {
+ display: block;
+ background-color: var(--background-color-primary);
+ padding: var(--spacing-l) var(--spacing-xl) var(--spacing-m)
+ var(--spacing-xl);
+ border-bottom: 1px solid var(--border-color);
+ }
+ .headerTopRow,
+ .headerBottomRow {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-end;
+ }
+ .headerTopRow gr-dropdown-list {
+ border: 1px solid var(--border-color);
+ border-radius: var(--border-radius);
+ padding: 0 var(--spacing-m);
+ }
+ .headerBottomRow {
+ margin-top: var(--spacing-s);
+ }
+ .headerBottomRow .right {
+ display: flex;
+ align-items: center;
+ }
+ #moreActions iron-icon {
+ color: var(--link-color);
+ }
+ #moreMessage {
+ display: none;
+ }
+ .body {
+ display: block;
+ padding: var(--spacing-s) var(--spacing-xl) var(--spacing-xl)
+ var(--spacing-xl);
}
.filterDiv {
display: flex;
@@ -463,7 +542,7 @@
}
}
- scrollElIntoView(selector: string) {
+ private scrollElIntoView(selector: string) {
this.updateComplete.then(() => {
let el = this.shadowRoot?.querySelector(selector);
// <gr-result-row> has display:contents and cannot be scrolled into view
@@ -475,13 +554,92 @@
render() {
return html`
- <div><h2 class="heading-2">Results</h2></div>
- ${this.renderFilter()} ${this.renderSection(Category.ERROR)}
- ${this.renderSection(Category.WARNING)}
- ${this.renderSection(Category.INFO)} ${this.renderSection('SUCCESS')}
+ <div class="header">
+ <div class="headerTopRow">
+ <div class="left">
+ <h2 class="heading-2">Results</h2>
+ </div>
+ <div class="middle">
+ <span ?hidden="${!this.someProvidersAreLoading}">Loading...</span>
+ </div>
+ <div class="right">
+ <gr-dropdown-list
+ value="${this.checksPatchsetNumber}"
+ .items="${this.createPatchsetDropdownItems()}"
+ @value-change="${this.onPatchsetSelected}"
+ ></gr-dropdown-list>
+ </div>
+ </div>
+ <div class="headerBottomRow">
+ <div class="left">
+ ${this.renderFilter()}
+ </div>
+ <div class="right">
+ ${this.renderActions()}
+ </div>
+ </div>
+ </div>
+ <div class="body">
+ ${this.renderSection(Category.ERROR)}
+ ${this.renderSection(Category.WARNING)}
+ ${this.renderSection(Category.INFO)} ${this.renderSection('SUCCESS')}
+ </div>
`;
}
+ private renderActions() {
+ const overflowItems = this.actions.slice(2).map(action => {
+ return {...action, id: action.name};
+ });
+ return html`
+ ${this.renderAction(this.actions[0])}
+ ${this.renderAction(this.actions[1])}
+ <gr-dropdown
+ id="moreActions"
+ link=""
+ vertical-offset="32"
+ horizontal-align="right"
+ @tap-item="${this.handleAction}"
+ ?hidden="${overflowItems.length === 0}"
+ .items="${overflowItems}"
+ >
+ <iron-icon icon="gr-icons:more-vert" aria-labelledby="moreMessage">
+ </iron-icon>
+ <span id="moreMessage">More</span>
+ </gr-dropdown>
+ `;
+ }
+
+ private handleAction(e: CustomEvent<Action>) {
+ fireActionTriggered(this, e.detail);
+ }
+
+ private renderAction(action?: Action) {
+ if (!action) return;
+ return html`<gr-checks-top-level-action
+ .action="${action}"
+ ></gr-checks-top-level-action>`;
+ }
+
+ private onPatchsetSelected(e: CustomEvent<{value: string}>) {
+ const patchset = Number(e.detail.value);
+ check(!isNaN(patchset), 'selected patchset must be a number');
+ this.checksService.setPatchset(patchset as PatchSetNumber);
+ }
+
+ private createPatchsetDropdownItems() {
+ if (!this.latestPatchsetNumber) return [];
+ return Array.from(Array(this.latestPatchsetNumber), (_, i) => {
+ assertIsDefined(this.latestPatchsetNumber, 'latestPatchsetNumber');
+ const index = this.latestPatchsetNumber - i;
+ const postfix = index === this.latestPatchsetNumber ? ' (latest)' : '';
+ return {
+ value: `${index}`,
+ text: `Patchset ${index}${postfix}`,
+ };
+ });
+ }
+
renderFilter() {
if (this.selectedRunsCount === 0 && allResults(this.runs).length <= 3) {
if (this.filterRegExp.source.length > 0) {
@@ -667,10 +825,50 @@
}
}
+@customElement('gr-checks-top-level-action')
+export class GrChecksTopLevelAction extends GrLitElement {
+ @property()
+ action!: Action;
+
+ connectedCallback() {
+ super.connectedCallback();
+ checkRequiredProperty(this.action, 'action');
+ }
+
+ static get styles() {
+ return [
+ css`
+ gr-button {
+ --padding: var(--spacing-s) var(--spacing-m);
+ }
+ gr-button paper-tooltip {
+ text-transform: none;
+ }
+ `,
+ ];
+ }
+
+ render() {
+ return html`
+ <gr-button link class="action" @click="${this.handleClick}">
+ ${this.action.name}
+ <paper-tooltip ?hidden="${!this.action.tooltip}" offset="5"
+ >${this.action.tooltip}</paper-tooltip
+ >
+ </gr-button>
+ `;
+ }
+
+ handleClick() {
+ fireActionTriggered(this, this.action);
+ }
+}
+
declare global {
interface HTMLElementTagNameMap {
'gr-result-row': GrResultRow;
'gr-result-expanded': GrResultExpanded;
'gr-checks-results': GrChecksResults;
+ 'gr-checks-top-level-action': GrChecksTopLevelAction;
}
}
diff --git a/polygerrit-ui/app/elements/checks/gr-checks-runs.ts b/polygerrit-ui/app/elements/checks/gr-checks-runs.ts
index c531874..3b07cf2 100644
--- a/polygerrit-ui/app/elements/checks/gr-checks-runs.ts
+++ b/polygerrit-ui/app/elements/checks/gr-checks-runs.ts
@@ -38,6 +38,7 @@
import {
CheckRun,
allRuns$,
+ fakeActions,
fakeRun0,
fakeRun1,
fakeRun2,
@@ -258,7 +259,8 @@
css`
:host {
display: block;
- padding: var(--spacing-xl);
+ padding: var(--spacing-l) var(--spacing-xl) var(--spacing-xl)
+ var(--spacing-xl);
}
.expandIcon {
width: var(--line-height-h3);
@@ -280,7 +282,7 @@
padding-bottom: var(--spacing-m);
}
input#filterInput {
- margin-top: var(--spacing-s);
+ margin-top: var(--spacing-m);
padding: var(--spacing-s) var(--spacing-m);
width: 100%;
}
@@ -334,7 +336,9 @@
<div class="testing">
<div>Toggle fake runs by clicking buttons:</div>
<gr-button link @click="${this.none}">none</gr-button>
- <gr-button link @click="${() => this.toggle('f0', fakeRun0)}"
+ <gr-button
+ link
+ @click="${() => this.toggle('f0', fakeRun0, fakeActions)}"
>0</gr-button
>
<gr-button link @click="${() => this.toggle('f1', fakeRun1)}"
@@ -360,7 +364,7 @@
}
none() {
- updateStateSetResults('f0', []);
+ updateStateSetResults('f0', [], []);
updateStateSetResults('f1', []);
updateStateSetResults('f2', []);
updateStateSetResults('f3', []);
@@ -368,16 +372,16 @@
}
all() {
- updateStateSetResults('f0', [fakeRun0]);
+ updateStateSetResults('f0', [fakeRun0], fakeActions);
updateStateSetResults('f1', [fakeRun1]);
updateStateSetResults('f2', [fakeRun2]);
updateStateSetResults('f3', [fakeRun3]);
updateStateSetResults('f4', [fakeRun4]);
}
- toggle(plugin: string, run: CheckRun) {
+ toggle(plugin: string, run: CheckRun, actions: Action[] = []) {
const newRuns = this.runs.includes(run) ? [] : [run];
- updateStateSetResults(plugin, newRuns);
+ updateStateSetResults(plugin, newRuns, actions);
}
renderSection(status: RunStatus) {
diff --git a/polygerrit-ui/app/elements/checks/gr-checks-tab.ts b/polygerrit-ui/app/elements/checks/gr-checks-tab.ts
index 6e11265..d017eb7 100644
--- a/polygerrit-ui/app/elements/checks/gr-checks-tab.ts
+++ b/polygerrit-ui/app/elements/checks/gr-checks-tab.ts
@@ -27,26 +27,15 @@
import {
CheckResult,
CheckRun,
- allActions$,
allResults$,
allRuns$,
checksPatchsetNumber$,
- someProvidersAreLoading$,
} from '../../services/checks/checks-model';
import './gr-checks-runs';
import './gr-checks-results';
-import {sharedStyles} from '../../styles/shared-styles';
-import {changeNum$, latestPatchNum$} from '../../services/change/change-model';
+import {changeNum$} from '../../services/change/change-model';
import {NumericChangeId, PatchSetNumber} from '../../types/common';
-import {
- ActionTriggeredEvent,
- fireActionTriggered,
-} from '../../services/checks/checks-util';
-import {
- assertIsDefined,
- check,
- checkRequiredProperty,
-} from '../../utils/common-util';
+import {ActionTriggeredEvent} from '../../services/checks/checks-util';
import {RunSelectedEvent} from './gr-checks-util';
import {ChecksTabState} from '../../types/events';
import {fireAlert} from '../../utils/event-util';
@@ -63,10 +52,9 @@
@property()
runs: CheckRun[] = [];
+ @property()
results: CheckResult[] = [];
- actions: Action[] = [];
-
@property()
tabState?: ChecksTabState;
@@ -74,14 +62,8 @@
checksPatchsetNumber: PatchSetNumber | undefined = undefined;
@property()
- latestPatchsetNumber: PatchSetNumber | undefined = undefined;
-
- @property()
changeNum: NumericChangeId | undefined = undefined;
- @property()
- someProvidersAreLoading = false;
-
@internalProperty()
selectedRuns: string[] = [];
@@ -90,12 +72,9 @@
constructor() {
super();
this.subscribe('runs', allRuns$);
- this.subscribe('actions', allActions$);
this.subscribe('results', allResults$);
this.subscribe('checksPatchsetNumber', checksPatchsetNumber$);
- this.subscribe('latestPatchsetNumber', latestPatchNum$);
this.subscribe('changeNum', changeNum$);
- this.subscribe('someProvidersAreLoading', someProvidersAreLoading$);
this.addEventListener('action-triggered', (e: ActionTriggeredEvent) =>
this.handleActionTriggered(e.detail.action, e.detail.run)
@@ -103,35 +82,22 @@
}
static get styles() {
- return [
- sharedStyles,
- css`
- :host {
- display: block;
- }
- .header {
- display: flex;
- justify-content: space-between;
- padding: var(--spacing-m) var(--spacing-l);
- border-bottom: 1px solid var(--border-color);
- }
- .action {
- margin-left: var(--spacing-m);
- }
- .container {
- display: flex;
- }
- .runs {
- min-width: 300px;
- min-height: 400px;
- border-right: 1px solid var(--border-color);
- }
- .results {
- background-color: var(--background-color-secondary);
- flex-grow: 1;
- }
- `,
- ];
+ return css`
+ :host {
+ display: block;
+ }
+ .container {
+ display: flex;
+ }
+ .runs {
+ min-width: 300px;
+ min-height: 400px;
+ border-right: 1px solid var(--border-color);
+ }
+ .results {
+ flex-grow: 1;
+ }
+ `;
}
render() {
@@ -141,19 +107,6 @@
this.selectedRuns.includes(r.checkName)
);
return html`
- <div class="header">
- <div class="left">
- <gr-dropdown-list
- value="${this.checksPatchsetNumber}"
- .items="${this.createPatchsetDropdownItems()}"
- @value-change="${this.onPatchsetSelected}"
- ></gr-dropdown-list>
- <span ?hidden="${!this.someProvidersAreLoading}">Loading...</span>
- </div>
- <div class="right">
- ${this.actions.map(this.renderAction)}
- </div>
- </div>
<div class="container">
<gr-checks-runs
class="runs"
@@ -173,25 +126,6 @@
`;
}
- private onPatchsetSelected(e: CustomEvent<{value: string}>) {
- const patchset = Number(e.detail.value);
- check(!isNaN(patchset), 'selected patchset must be a number');
- this.checksService.setPatchset(patchset as PatchSetNumber);
- }
-
- private createPatchsetDropdownItems() {
- if (!this.latestPatchsetNumber) return [];
- return Array.from(Array(this.latestPatchsetNumber), (_, i) => {
- assertIsDefined(this.latestPatchsetNumber, 'latestPatchsetNumber');
- const index = this.latestPatchsetNumber - i;
- const postfix = index === this.latestPatchsetNumber ? ' (latest)' : '';
- return {
- value: `${index}`,
- text: `Patchset ${index}${postfix}`,
- };
- });
- }
-
protected updated(changedProperties: PropertyValues) {
super.updated(changedProperties);
if (changedProperties.has('tabState')) {
@@ -201,12 +135,6 @@
}
}
- renderAction(action: Action) {
- return html`<gr-checks-top-level-action
- .action="${action}"
- ></gr-checks-top-level-action>`;
- }
-
handleActionTriggered(action: Action, run?: CheckRun) {
if (!this.changeNum) return;
if (!this.checksPatchsetNumber) return;
@@ -258,32 +186,8 @@
}
}
-@customElement('gr-checks-top-level-action')
-export class GrChecksTopLevelAction extends GrLitElement {
- @property()
- action!: Action;
-
- connectedCallback() {
- super.connectedCallback();
- checkRequiredProperty(this.action, 'action');
- }
-
- render() {
- return html`
- <gr-button link class="action" @click="${this.handleClick}"
- >${this.action.name}</gr-button
- >
- `;
- }
-
- handleClick() {
- fireActionTriggered(this, this.action);
- }
-}
-
declare global {
interface HTMLElementTagNameMap {
'gr-checks-tab': GrChecksTab;
- 'gr-checks-top-level-action': GrChecksTopLevelAction;
}
}
diff --git a/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown_html.ts b/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown_html.ts
index c767edd..b1fa7bd 100644
--- a/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown_html.ts
+++ b/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown_html.ts
@@ -30,6 +30,7 @@
box-shadow: var(--elevation-level-2);
}
gr-button {
+ vertical-align: top;
@apply --gr-button;
}
gr-avatar {
diff --git a/polygerrit-ui/app/services/checks/checks-model.ts b/polygerrit-ui/app/services/checks/checks-model.ts
index 302d0f1..81478bf 100644
--- a/polygerrit-ui/app/services/checks/checks-model.ts
+++ b/polygerrit-ui/app/services/checks/checks-model.ts
@@ -241,6 +241,36 @@
status: RunStatus.COMPLETED,
};
+export const fakeActions: Action[] = [
+ {
+ name: 'Fake Action 1',
+ primary: false,
+ tooltip: 'Tooltip for Fake Action 1',
+ callback: () => {
+ console.warn('fake action 1 triggered');
+ return undefined;
+ },
+ },
+ {
+ name: 'Fake Action 2',
+ primary: false,
+ tooltip: 'Tooltip for Fake Action 2',
+ callback: () => {
+ console.warn('fake action 2 triggered');
+ return undefined;
+ },
+ },
+ {
+ name: 'Fake Action 3',
+ primary: false,
+ tooltip: 'Tooltip for Fake Action 3',
+ callback: () => {
+ console.warn('fake action 3 triggered');
+ return undefined;
+ },
+ },
+];
+
export function updateStateSetLoading(pluginName: string) {
const nextState = {...privateState$.getValue()};
nextState.providerNameToState = {...nextState.providerNameToState};