Add support for top-level actions
Change-Id: Id70362187a716f3e5b2080f0b40920c5f5c92dd7
diff --git a/polygerrit-ui/app/api/checks.ts b/polygerrit-ui/app/api/checks.ts
index 143fbd1..361b8e0 100644
--- a/polygerrit-ui/app/api/checks.ts
+++ b/polygerrit-ui/app/api/checks.ts
@@ -224,10 +224,21 @@
export type ActionCallback = (
change: number,
patchset: number,
+ /**
+ * Identical to 'attempt' property of CheckRun. Not set for top-level
+ * actions.
+ */
attempt: number | undefined,
+ /**
+ * Identical to 'externalId' property of CheckRun. Not set for top-level
+ * actions.
+ */
externalId: string | undefined,
- /** Identical to 'checkName' property of CheckRun. */
- checkName: string,
+ /**
+ * Identical to 'checkName' property of CheckRun. Not set for top-level
+ * actions.
+ */
+ checkName: string | undefined,
/** Identical to 'name' property of Action entity. */
actionName: string
) => Promise<ActionResult>;
diff --git a/polygerrit-ui/app/elements/checks/gr-checks-results.ts b/polygerrit-ui/app/elements/checks/gr-checks-results.ts
index 007af97..69521ff 100644
--- a/polygerrit-ui/app/elements/checks/gr-checks-results.ts
+++ b/polygerrit-ui/app/elements/checks/gr-checks-results.ts
@@ -81,7 +81,7 @@
}
td .summary-cell {
display: flex;
- max-width: calc(100vw - 579px);
+ max-width: calc(100vw - 700px);
}
td .summary-cell .summary {
font-weight: var(--font-weight-bold);
diff --git a/polygerrit-ui/app/elements/checks/gr-checks-runs.ts b/polygerrit-ui/app/elements/checks/gr-checks-runs.ts
index 4a34700..5b7428b 100644
--- a/polygerrit-ui/app/elements/checks/gr-checks-runs.ts
+++ b/polygerrit-ui/app/elements/checks/gr-checks-runs.ts
@@ -22,6 +22,7 @@
import {sharedStyles} from '../../styles/shared-styles';
import {
compareByWorstCategory,
+ fireActionTriggered,
iconForRun,
primaryRunAction,
} from '../../services/checks/checks-util';
@@ -60,33 +61,6 @@
);
}
-export interface ActionTriggeredEventDetail {
- action: Action;
- run: CheckRun;
-}
-
-export type ActionTriggeredEvent = CustomEvent<ActionTriggeredEventDetail>;
-
-declare global {
- interface HTMLElementEventMap {
- 'action-triggered': ActionTriggeredEvent;
- }
-}
-
-function fireActionTriggered(
- target: EventTarget,
- action: Action,
- run: CheckRun
-) {
- target.dispatchEvent(
- new CustomEvent('action-triggered', {
- detail: {action, run},
- composed: true,
- bubbles: true,
- })
- );
-}
-
@customElement('gr-checks-run')
export class GrChecksRun extends GrLitElement {
static get styles() {
@@ -106,6 +80,11 @@
margin-top: var(--spacing-s);
cursor: pointer;
}
+ .left {
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ }
.name {
font-weight: var(--font-weight-bold);
}
diff --git a/polygerrit-ui/app/elements/checks/gr-checks-tab.ts b/polygerrit-ui/app/elements/checks/gr-checks-tab.ts
index d31ec53..1abe631 100644
--- a/polygerrit-ui/app/elements/checks/gr-checks-tab.ts
+++ b/polygerrit-ui/app/elements/checks/gr-checks-tab.ts
@@ -18,13 +18,20 @@
import {css, customElement, property} from 'lit-element';
import {GrLitElement} from '../lit/gr-lit-element';
import {Action, CheckResult, CheckRun} from '../../api/checks';
-import {allResults$, allRuns$} from '../../services/checks/checks-model';
+import {
+ allActions$,
+ allResults$,
+ allRuns$,
+} from '../../services/checks/checks-model';
import './gr-checks-runs';
import './gr-checks-results';
import {sharedStyles} from '../../styles/shared-styles';
import {changeNum$, currentPatchNum$} from '../../services/change/change-model';
import {NumericChangeId, PatchSetNum} from '../../types/common';
-import {ActionTriggeredEvent} from './gr-checks-runs';
+import {
+ ActionTriggeredEvent,
+ fireActionTriggered,
+} from '../../services/checks/checks-util';
/**
* The "Checks" tab on the Gerrit change page. Gets its data from plugins that
@@ -37,6 +44,8 @@
results: CheckResult[] = [];
+ actions: Action[] = [];
+
@property()
currentPatchNum: PatchSetNum | undefined = undefined;
@@ -46,6 +55,7 @@
constructor() {
super();
this.subscribe('runs', allRuns$);
+ this.subscribe('actions', allActions$);
this.subscribe('results', allResults$);
this.subscribe('currentPatchNum', currentPatchNum$);
this.subscribe('changeNum', changeNum$);
@@ -63,17 +73,13 @@
display: block;
}
.header {
- display: block;
+ display: flex;
+ justify-content: space-between;
padding: var(--spacing-m) var(--spacing-l);
border-bottom: 1px solid var(--border-color);
}
- .header span {
- display: inline-block;
- color: var(--link-color);
- padding: var(--spacing-s) var(--spacing-m);
- margin-right: var(--spacing-l);
- border: 1px solid var(--border-color);
- border-radius: var(--border-radius);
+ .action {
+ margin-left: var(--spacing-m);
}
.container {
display: flex;
@@ -95,15 +101,20 @@
const ps = `Patchset ${this.currentPatchNum} (Latest)`;
return html`
<div class="header">
- <gr-dropdown-list
- value="${ps}"
- .items="${[
- {
- value: `${ps}`,
- text: `${ps}`,
- },
- ]}"
- ></gr-dropdown-list>
+ <div class="left">
+ <gr-dropdown-list
+ value="${ps}"
+ .items="${[
+ {
+ value: `${ps}`,
+ text: `${ps}`,
+ },
+ ]}"
+ ></gr-dropdown-list>
+ </div>
+ <div class="right">
+ ${this.actions.map(this.renderAction)}
+ </div>
</div>
<div class="container">
<gr-checks-runs class="runs" .runs="${this.runs}"></gr-checks-runs>
@@ -115,7 +126,13 @@
`;
}
- private handleActionTriggered(action: Action, run: CheckRun) {
+ 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.currentPatchNum) return;
// TODO(brohlfs): The callback is supposed to be returning a promise.
@@ -124,16 +141,35 @@
action.callback(
this.changeNum,
this.currentPatchNum as number,
- run.attempt,
- run.externalId,
- run.checkName,
+ run?.attempt,
+ run?.externalId,
+ run?.checkName,
action.name
);
}
}
+@customElement('gr-checks-top-level-action')
+export class GrChecksTopLevelAction extends GrLitElement {
+ @property()
+ 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/services/checks/checks-model.ts b/polygerrit-ui/app/services/checks/checks-model.ts
index 9546d0b..6705a85 100644
--- a/polygerrit-ui/app/services/checks/checks-model.ts
+++ b/polygerrit-ui/app/services/checks/checks-model.ts
@@ -17,6 +17,7 @@
import {BehaviorSubject, Observable} from 'rxjs';
import {
+ Action,
Category,
CheckResult,
CheckRun,
@@ -35,6 +36,7 @@
pluginName: string;
config?: ChecksApiConfig;
runs: CheckRun[];
+ actions: Action[];
}
interface ChecksState {
@@ -53,6 +55,18 @@
distinctUntilChanged()
);
+export const allActions$ = checksState$.pipe(
+ map(state => {
+ return Object.values(state).reduce(
+ (allActions: Action[], providerState: ChecksProviderState) => [
+ ...allActions,
+ ...providerState.actions,
+ ],
+ []
+ );
+ })
+);
+
export const allRuns$ = checksState$.pipe(
map(state => {
return Object.values(state).reduce(
@@ -94,6 +108,7 @@
pluginName,
config,
runs: [],
+ actions: [],
};
privateState$.next(nextState);
}
@@ -162,11 +177,16 @@
status: RunStatus.COMPLETED,
};
-export function updateStateSetResults(pluginName: string, runs: CheckRun[]) {
+export function updateStateSetResults(
+ pluginName: string,
+ runs: CheckRun[],
+ actions: Action[] = []
+) {
const nextState = {...privateState$.getValue()};
nextState[pluginName] = {
...nextState[pluginName],
runs: [...runs],
+ actions: [...actions],
};
privateState$.next(nextState);
}
diff --git a/polygerrit-ui/app/services/checks/checks-service.ts b/polygerrit-ui/app/services/checks/checks-service.ts
index a8dd8b8..6b106ad 100644
--- a/polygerrit-ui/app/services/checks/checks-service.ts
+++ b/polygerrit-ui/app/services/checks/checks-service.ts
@@ -83,7 +83,11 @@
}
),
tap(response => {
- updateStateSetResults(pluginName, response.runs ?? []);
+ updateStateSetResults(
+ pluginName,
+ response.runs ?? [],
+ response.actions
+ );
})
)
.subscribe(() => {});
diff --git a/polygerrit-ui/app/services/checks/checks-util.ts b/polygerrit-ui/app/services/checks/checks-util.ts
index 30b82b6..176464f 100644
--- a/polygerrit-ui/app/services/checks/checks-util.ts
+++ b/polygerrit-ui/app/services/checks/checks-util.ts
@@ -135,3 +135,30 @@
return 2;
}
}
+
+export interface ActionTriggeredEventDetail {
+ action: Action;
+ run?: CheckRun;
+}
+
+export type ActionTriggeredEvent = CustomEvent<ActionTriggeredEventDetail>;
+
+declare global {
+ interface HTMLElementEventMap {
+ 'action-triggered': ActionTriggeredEvent;
+ }
+}
+
+export function fireActionTriggered(
+ target: EventTarget,
+ action: Action,
+ run?: CheckRun
+) {
+ target.dispatchEvent(
+ new CustomEvent('action-triggered', {
+ detail: {action, run},
+ composed: true,
+ bubbles: true,
+ })
+ );
+}