Add ability for plugins to add columns to the change list

Adds registerDynamicPluginEndpoint to Gerrit. Dynamic plugin endpoints are
registered to the dynamic prefix. The two supported dynamic endpoints are
'change-list-header' and 'change-list-item-cell'.

Change-Id: I0e45f1767e39da004933c48b633fc977724bf863
This commit is contained in:
Thomas Shafer
2019-02-26 15:39:16 -08:00
parent 3a9f8cb431
commit 032758670d
9 changed files with 109 additions and 5 deletions

View File

@@ -177,6 +177,14 @@ See list of supported link:pg-plugin-endpoints.html[endpoints].
Note: TODO
=== registerDynamicCustomComponent
`plugin.registerDynamicCustomComponent(dynamicEndpointName, opt_moduleName,
opt_options)`
See list of supported link:pg-plugin-endpoints.html[endpoints].
Note: TODO
=== registerStyleModule
`plugin.registerStyleModule(endpointName, moduleName)`

View File

@@ -141,3 +141,19 @@ link:rest-api-changes.html#change-info[ChangeInfo]
+
The submit action, including the title and label, an instance of
link:rest-api-changes.html#action-info[ActionInfo]
== Dynamic Plugin endpoints
The following endpoints are available to plugins.
=== change-list-header
The `change-list-header` extension point adds a header to the change list view.
=== change-list-item-cell
The `change-list-item-cell` extension point adds a cell to the change list item.
* `change`
+
current change of the row, an instance of
link:rest-api-changes.html#change-info[ChangeInfo]

View File

@@ -222,6 +222,15 @@ limitations under the License.
[[_computeLabelValue(change, labelName)]]
</td>
</template>
<template is="dom-repeat" items="[[_dynamicCellEndpoints]]"
as="pluginEndpointName">
<td class="cell endpoint">
<gr-endpoint-decorator name$="[[pluginEndpointName]]">
<gr-endpoint-param name="change" value="[[change]]">
</gr-endpoint-param>
</gr-endpoint-decorator>
</td>
</template>
</template>
<script src="gr-change-list-item.js"></script>
</dom-module>

View File

@@ -57,6 +57,9 @@
type: String,
computed: '_computeChangeSize(change)',
},
_dynamicCellEndpoints: {
type: Array,
},
},
behaviors: [
@@ -67,6 +70,13 @@
Gerrit.URLEncodingBehavior,
],
attached() {
Gerrit.awaitPluginsLoaded().then(() => {
this._dynamicCellEndpoints = Gerrit._endpoints.getDynamicEndpoints(
'change-list-item-cell');
});
},
_computeItemNeedsReview(reviewed) {
return !reviewed;
},

View File

@@ -52,6 +52,13 @@ limitations under the License.
[[_computeLabelShortcut(labelName)]]
</th>
</template>
<template is="dom-repeat" items="[[_dynamicHeaderEndpoints]]"
as="pluginHeader">
<th class="endpoint">
<gr-endpoint-decorator name$="[[pluginHeader]]">
</gr-endpoint-decorator>
</th>
</template>
</tr>
<template is="dom-repeat" items="[[sections]]" as="changeSection"
index-as="sectionIndex">

View File

@@ -76,6 +76,9 @@
type: Array,
computed: '_computeLabelNames(sections)',
},
_dynamicHeaderEndpoints: {
type: Array,
},
selectedIndex: {
type: Number,
notify: true,
@@ -128,6 +131,13 @@
};
},
attached() {
Gerrit.awaitPluginsLoaded().then(() => {
this._dynamicHeaderEndpoints = Gerrit._endpoints.getDynamicEndpoints(
'change-list-header');
});
},
/**
* Iron-a11y-keys-behavior catches keyboard events globally. Some keyboard
* events must be scoped to a component level (e.g. `enter`) in order to not

View File

@@ -20,6 +20,7 @@
function GrPluginEndpoints() {
this._endpoints = {};
this._callbacks = {};
this._dynamicPlugins = {};
}
GrPluginEndpoints.prototype.onNewEndpoint = function(endpoint, callback) {
@@ -51,8 +52,21 @@
}
};
/**
* Register a plugin to an endpoint.
*
* Dynamic plugins are registered to a specific prefix, such as
* 'change-list-header'. These plugins are then fetched by prefix to determine
* which endpoints to dynamically add to the page.
*/
GrPluginEndpoints.prototype.registerModule = function(plugin, endpoint, type,
moduleName, domHook) {
moduleName, domHook, dynamicEndpoint) {
if (dynamicEndpoint) {
if (!this._dynamicPlugins[dynamicEndpoint]) {
this._dynamicPlugins[dynamicEndpoint] = new Set();
}
this._dynamicPlugins[dynamicEndpoint].add(endpoint);
}
if (!this._endpoints[endpoint]) {
this._endpoints[endpoint] = [];
}
@@ -63,6 +77,12 @@
}
};
GrPluginEndpoints.prototype.getDynamicEndpoints = function(dynamicEndpoint) {
const plugins = this._dynamicPlugins[dynamicEndpoint];
if (!plugins) return [];
return Array.from(plugins);
};
/**
* Get detailed information about modules registered with an extension
* endpoint.

View File

@@ -189,14 +189,36 @@
this, endpointName, EndpointType.STYLE, moduleName);
};
/**
* Registers an endpoint for the plugin.
*/
Plugin.prototype.registerCustomComponent = function(
endpointName, opt_moduleName, opt_options) {
return this._registerCustomComponent(endpointName, opt_moduleName,
opt_options);
};
/**
* Registers a dynamic endpoint for the plugin.
*
* Dynamic plugins are registered by specific prefix, such as
* 'change-list-header'.
*/
Plugin.prototype.registerDynamicCustomComponent = function(
endpointName, opt_moduleName, opt_options) {
const fullEndpointName = `${endpointName}-${this.getPluginName()}`;
return this._registerCustomComponent(fullEndpointName, opt_moduleName,
opt_options, endpointName);
};
Plugin.prototype._registerCustomComponent = function(
endpointName, opt_moduleName, opt_options, dynamicEndpoint) {
const type = opt_options && opt_options.replace ?
EndpointType.REPLACE : EndpointType.DECORATE;
const hook = this._domHooks.getDomHook(endpointName, opt_moduleName);
const moduleName = opt_moduleName || hook.getModuleName();
Gerrit._endpoints.registerModule(
this, endpointName, type, moduleName, hook);
this, endpointName, type, moduleName, hook, dynamicEndpoint);
return hook.getPublicAPI();
};

View File

@@ -55,8 +55,8 @@ limitations under the License.
.cell {
vertical-align: middle;
}
th:not(.label),
.cell:not(.label) {
th:not(.label):not(.endpoint),
.cell:not(.label):not(.endpoint) {
padding-right: 8px;
}
th.label {
@@ -128,8 +128,10 @@ limitations under the License.
.star {
width: 30px;
}
.label {
.label, .endpoint {
border-left: 1px solid var(--border-color);
}
.label {
text-align: center;
width: 3rem;
}