Caution
Work in progress. Hard hat area. Please send feedback if something’s not right.

For migrating existing GWT UI plugins, please check out the migration guide.

Plugin loading and initialization

Entry point for the plugin and the loading method is based on HTML Imports spec.

  • The plugin provides pluginname.html, and can be a standalone file or a static asset in a jar as a Web UI plugin.

  • pluginname.html contains a dom-module tag with a script that uses Gerrit.install(). There should only be single Gerrit.install() per file.

  • PolyGerrit imports pluginname.html along with all required resources defined in it (fonts, styles, etc).

  • For standalone plugins, the entry point file is a pluginname.html file located in gerrit-site/plugins folder, where pluginname is an alphanumeric plugin name.

Note: Code examples target modern browsers (Chrome, Firefox, Safari, Edge).

Here’s a recommended starter myplugin.html:

<dom-module id="my-plugin">
  <script>
    Gerrit.install(plugin => {
      'use strict';

      // Your code here.
    });
  </script>
</dom-module>

Low-level DOM API concepts

Basically, the DOM is the API surface. Low-level API provides methods for decorating, replacing, and styling DOM elements exposed through a set of endpoints.

PolyGerrit provides a simple way for accessing the DOM via DOM hooks API. A DOM hook is a custom element that is instantiated for the plugin endpoint. In the decoration case, a hook is set with a content attribute that points to the DOM element.

  1. Get the DOM hook API instance via plugin.hook(endpointName)

  2. Set up an onAttached callback

  3. Callback is called when the hook element is created and inserted into DOM

  4. Use element.content to get UI element

Gerrit.install(plugin => {
  const domHook = plugin.hook('reply-text');
  domHook.onAttached(element => {
    if (!element.content) { return; }
    // element.content is a reply dialog text area.
  });
});

Decorating DOM Elements

For each endpoint, PolyGerrit provides a list of DOM properties (such as attributes and events) that are supported in the long-term.

Gerrit.install(plugin => {
  const domHook = plugin.hook('reply-text');
  domHook.onAttached(element => {
    if (!element.content) { return; }
    element.content.style.border = '1px red dashed';
  });
});

Replacing DOM Elements

An endpoint’s contents can be replaced by passing the replace attribute as an option.

Gerrit.install(plugin => {
  const domHook = plugin.hook('header-title', {replace: true});
  domHook.onAttached(element => {
    element.appendChild(document.createElement('my-site-header'));
  });
});

Styling DOM Elements

A plugin may provide Polymer’s style modules to style individual endpoints using plugin.registerStyleModule(endpointName, moduleName). A style must be defined as a standalone <dom-module> defined in the same .html file.

Note: TODO: Insert link to the full styling API.

<dom-module id="my-plugin">
  <script>
    Gerrit.install(plugin => {
      plugin.registerStyleModule('change-metadata', 'some-style-module');
    });
  </script>
</dom-module>

<dom-module id="some-style-module">
  <style>
    html {
      --change-metadata-label-status: {
        display: none;
      }
      --change-metadata-strategy: {
        display: none;
      }
    }
  </style>
</dom-module>

High-level DOM API concepts

High level API is based on low-level DOM API and is essentially a standardized way for doing common tasks. It’s less flexible, but will be a bit more stable.

The common way to access high-level API is through plugin instance passed into setup callback parameter of Gerrit.install(), also sometimes referred to as self.

Low-level DOM API

The low-level DOM API methods are the base of all UI customization.

attributeHelper

plugin.attributeHelper(element)

Alternative for Polymer data binding for plugins that don’t use Polymer. Can be used to bind element attribute changes to callbacks.

See samples/bind-parameters.html for examples on both Polymer data bindings and attibuteHelper usage.

eventHelper

plugin.eventHelper(element)

Note: TODO

hook

plugin.hook(endpointName, opt_options)

See list of supported endpoints.

Note: TODO

registerCustomComponent

plugin.registerCustomComponent(endpointName, opt_moduleName, opt_options)

See list of supported endpoints.

Note: TODO

registerDynamicCustomComponent

plugin.registerDynamicCustomComponent(dynamicEndpointName, opt_moduleName, opt_options)

See list of supported endpoints.

Note: TODO

registerStyleModule

plugin.registerStyleModule(endpointName, moduleName)

Note: TODO

High-level API

Plugin instance provides access to number of more specific APIs and methods to be used by plugin authors.

admin

plugin.admin()

Params:
  • none

Returns:

changeReply

plugin.changeReply()

Note: TODO

changeView

plugin.changeView()

Note: TODO

delete

plugin.delete(url, opt_callback)

Note: TODO

get

plugin.get(url, opt_callback)

Note: TODO

getPluginName

plugin.getPluginName()

Note: TODO

getServerInfo

plugin.getServerInfo()

Note: TODO

on

plugin.on(eventName, callback)

Note: TODO

panel

plugin.panel(extensionpoint, callback)

Deprecated. Use plugin.registerCustomComponent() instead.

Gerrit.install(function(self) {
  self.panel('CHANGE_SCREEN_BELOW_COMMIT_INFO_BLOCK', function(context) {
    context.body.innerHTML =
      'Sample link: <a href="http://some.com/foo">Foo</a>';
    context.show();
  });
});

Here’s the recommended approach that uses Polymer for generating custom elements:

<dom-module id="some-plugin">
  <script>
    Gerrit.install(plugin => {
      plugin.registerCustomComponent(
        'change-view-integration', 'some-ci-module');
    });
  </script>
</dom-module>

<dom-module id="some-ci-module">
  <template>
    Sample link: <a href="http://some.com/foo">Foo</a>
  </template>
  <script>
    Polymer({is: 'some-ci-module'});
  </script>
</dom-module>

Here’s a minimal example that uses low-level DOM Hooks API for the same purpose:

Gerrit.install(plugin => {
  plugin.hook('change-view-integration', el => {
    el.innerHTML = 'Sample link: <a href="http://some.com/foo">Foo</a>';
  });
});

popup

plugin.popup(moduleName)

Note: TODO

post

plugin.post(url, payload, opt_callback)

Note: TODO

restApi

plugin.restApi(opt_prefix)

Params:
  • (optional) URL prefix, for easy switching into plugin URL space, e.g. changes/1/revisions/1/cookbook~say-hello

Returns:

repo

plugin.repo()

Params:
  • none

Returns:

put

plugin.put(url, payload, opt_callback)

Note: TODO

screen

plugin.screen(screenName, opt_moduleName)

Params:
  • string screenName URL path fragment of the screen, e.g. /x/pluginname/screenname

  • string opt_moduleName (Optional) Web component to be instantiated for this screen.

Returns:
  • Instance of GrDomHook.

screenUrl

plugin.url(opt_screenName)

Params:
  • string screenName (optional) URL path fragment of the screen, e.g. /x/pluginname/screenname

Returns:

settings

plugin.settings()

Params:
  • none

Returns:

settingsScreen

plugin.settingsScreen(path, menu, callback)

Deprecated. Use plugin.settings() instead.

changeMetadata

plugin.changeMetadata()

Params:
  • none

Returns:

theme

plugin.theme()

Note: TODO

url

plugin.url(opt_path)

Note: TODO

Deprecated APIs

Some of the deprecated APIs have limited implementation in PolyGerrit to serve as a "stepping stone" to allow gradual migration.

install

plugin.deprecated.install()

Params:
  • none

Replaces plugin APIs with a deprecated version. This allows use of deprecated APIs without changing JS code. For example, onAction is not available by default, and after plugin.deprecated.install() it’s accessible via self.onAction().

onAction

plugin.deprecated.onAction(type, view_name, callback)

Params:
  • string type Action type.

  • string view_name REST API action.

  • function(actionContext) callback Callback invoked on action button click.

Adds a button to the UI with a click callback. Exact button location depends on parameters. Callback is triggered with an instance of action context.

Support is limited:

  • type is either change or revision.

See self.onAction for more info.

panel

plugin.deprecated.panel(extensionpoint, callback)

Params:
  • string extensionpoint

  • function(screenContext) callback

Adds a UI DOM element and triggers a callback with context to allow direct DOM access.

Support is limited:

  • extensionpoint is one of the following:

    • CHANGE_SCREEN_BELOW_COMMIT_INFO_BLOCK

    • CHANGE_SCREEN_BELOW_CHANGE_INFO_BLOCK

See self.panel for more info.

settingsScreen

plugin.deprecated.settingsScreent(path, menu, callback)

Params:
  • string path URL path fragment of the screen for direct link.

  • string menu Menu item title.

  • function(settingsScreenContext) callback

Adds a settings menu item and a section in the settings screen that is provided to plugin for setup.

See self.settingsScreen for more info.

Action Context (deprecated)

Instance of Action Context is passed to onAction() callback.

Support is limited:

  • popup()

  • hide()

  • refresh()

  • textfield()

  • br()

  • msg()

  • div()

  • button()

  • checkbox()

  • label()

  • prependLabel()

  • call()

See Action Context for more info.