Add a project command plugin endpoint, and a sample plugin
Adds an project-command plugin endpoint for plugins to provide own commands. Creates a common gr-project-command element for uniform appearance. Provides high-level project plugin API for typical use. project-command.html contains low-level plugin API example for advanced customization. Change-Id: I6e47e4724c9d5a5e783f8bb562fcd0a3fa342d6c
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
<!--
|
||||
Copyright (C) 2017 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.
|
||||
-->
|
||||
<link rel="import" href="../../../bower_components/polymer/polymer.html">
|
||||
<link rel="import" href="../../../styles/shared-styles.html">
|
||||
<link rel="import" href="../../shared/gr-button/gr-button.html">
|
||||
|
||||
<dom-module id="gr-project-command">
|
||||
<template>
|
||||
<style include="shared-styles">
|
||||
:host {
|
||||
display: block;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
</style>
|
||||
<h3>[[title]]</h3>
|
||||
<gr-button on-tap="_onCommandTap">[[title]]</gr-button>
|
||||
</template>
|
||||
<script src="gr-project-command.js"></script>
|
||||
</dom-module>
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright (C) 2017 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.
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
Polymer({
|
||||
is: 'gr-project-command',
|
||||
|
||||
properties: {
|
||||
title: String,
|
||||
},
|
||||
|
||||
/**
|
||||
* Fired when command button is tapped.
|
||||
*
|
||||
* @event command-tap
|
||||
*/
|
||||
|
||||
_onCommandTap() {
|
||||
this.dispatchEvent(new CustomEvent('command-tap', {bubbles: true}));
|
||||
},
|
||||
});
|
||||
})();
|
||||
@@ -0,0 +1,50 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
Copyright (C) 2017 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.
|
||||
-->
|
||||
|
||||
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
|
||||
<title>gr-project-command</title>
|
||||
|
||||
<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
|
||||
<script src="../../../bower_components/web-component-tester/browser.js"></script>
|
||||
<link rel="import" href="../../../test/common-test-setup.html"/>
|
||||
<link rel="import" href="gr-project-command.html">
|
||||
|
||||
<script>void(0);</script>
|
||||
|
||||
<test-fixture id="basic">
|
||||
<template>
|
||||
<gr-project-command></gr-project-command>
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
<script>
|
||||
suite('gr-project-command tests', () => {
|
||||
let element;
|
||||
|
||||
setup(() => {
|
||||
element = fixture('basic');
|
||||
});
|
||||
|
||||
test('dispatched command-tap on button tap', done => {
|
||||
element.addEventListener('command-tap', () => {
|
||||
done();
|
||||
});
|
||||
MockInteractions.tap(
|
||||
Polymer.dom(element.root).querySelector('gr-button'));
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -19,10 +19,12 @@ limitations under the License.
|
||||
<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
|
||||
<link rel="import" href="../../../styles/gr-form-styles.html">
|
||||
<link rel="import" href="../../../styles/shared-styles.html">
|
||||
<link rel="import" href="../../plugins/gr-endpoint-decorator/gr-endpoint-decorator.html">
|
||||
<link rel="import" href="../../shared/gr-confirm-dialog/gr-confirm-dialog.html">
|
||||
<link rel="import" href="../../shared/gr-overlay/gr-overlay.html">
|
||||
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
|
||||
<link rel="import" href="../gr-create-change-dialog/gr-create-change-dialog.html">
|
||||
<link rel="import" href="../gr-project-command/gr-project-command.html">
|
||||
|
||||
<dom-module id="gr-project-commands">
|
||||
<template>
|
||||
@@ -47,24 +49,23 @@ limitations under the License.
|
||||
<div id="loadedContent" class$="[[_computeLoadingClass(_loading)]]">
|
||||
<h2 id="options">Command</h2>
|
||||
<div id="form">
|
||||
<fieldset>
|
||||
<h3 id="createChange">Create Change</h3>
|
||||
<fieldset>
|
||||
<gr-button id="createNewChange" on-tap="_createNewChange">
|
||||
Create Change
|
||||
</gr-button>
|
||||
</fieldset>
|
||||
<h3 id="runGC" hidden$="[[!_projectConfig.actions.gc.enabled]]">
|
||||
Run GC
|
||||
</h3>
|
||||
<fieldset>
|
||||
<gr-button
|
||||
on-tap="_handleRunningGC"
|
||||
hidden$="[[!_projectConfig.actions.gc.enabled]]">
|
||||
Run GC
|
||||
</gr-button>
|
||||
</fieldset>
|
||||
</fieldset>
|
||||
<gr-project-command
|
||||
title="Create Change"
|
||||
on-command-tap="_createNewChange">
|
||||
</gr-project-command>
|
||||
|
||||
<gr-project-command
|
||||
title="Run GC"
|
||||
hidden$="[[!_projectConfig.actions.gc.enabled]]"
|
||||
on-command-tap="_handleRunningGC">
|
||||
</gr-project-command>
|
||||
|
||||
<gr-endpoint-decorator name="project-command">
|
||||
<gr-endpoint-param name="config" value="[[_projectConfig]]">
|
||||
</gr-endpoint-param>
|
||||
<gr-endpoint-param name="projectName" value="[[project]]">
|
||||
</gr-endpoint-param>
|
||||
</gr-endpoint-decorator>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@@ -19,6 +19,17 @@
|
||||
this._unsubscribers = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a callback to arbitrary event.
|
||||
* The callback may return false to prevent event bubbling.
|
||||
* @param {string} event Event name
|
||||
* @param {function(Event):boolean} callback
|
||||
* @return {function()} Unsubscribe function.
|
||||
*/
|
||||
GrEventHelper.prototype.on = function(event, callback) {
|
||||
return this._listen(this.element, callback, {event});
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a callback to element click or touch.
|
||||
* The callback may return false to prevent event bubbling.
|
||||
@@ -43,6 +54,7 @@
|
||||
|
||||
GrEventHelper.prototype._listen = function(container, callback, opt_options) {
|
||||
const capture = opt_options && opt_options.capture;
|
||||
const event = opt_options && opt_options.event || 'tap';
|
||||
const handler = e => {
|
||||
if (e.path.indexOf(this.element) !== -1) {
|
||||
let mayContinue = true;
|
||||
@@ -58,9 +70,9 @@
|
||||
}
|
||||
}
|
||||
};
|
||||
container.addEventListener('tap', handler, capture);
|
||||
container.addEventListener(event, handler, capture);
|
||||
const unsubscribe = () =>
|
||||
container.removeEventListener('tap', handler, capture);
|
||||
container.removeEventListener(event, handler, capture);
|
||||
this._unsubscribers.push(unsubscribe);
|
||||
return unsubscribe;
|
||||
};
|
||||
|
||||
@@ -92,5 +92,12 @@ limitations under the License.
|
||||
flushAsynchronousOperations();
|
||||
assert.isFalse(tapStub.called);
|
||||
});
|
||||
|
||||
test('on()', done => {
|
||||
instance.on('foo', () => {
|
||||
done();
|
||||
});
|
||||
element.fire('foo');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<!--
|
||||
Copyright (C) 2017 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.
|
||||
-->
|
||||
<link rel="import" href="../../../bower_components/polymer/polymer.html">
|
||||
<link rel="import" href="../../admin/gr-project-command/gr-project-command.html">
|
||||
|
||||
<dom-module id="gr-plugin-project-command">
|
||||
<template>
|
||||
<gr-project-command title="[[title]]">
|
||||
</gr-project-command>
|
||||
</template>
|
||||
<script>
|
||||
Polymer({
|
||||
is: 'gr-plugin-project-command',
|
||||
properties: {
|
||||
title: String,
|
||||
projectName: String,
|
||||
config: Object,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
</dom-module>
|
||||
@@ -0,0 +1,23 @@
|
||||
<!--
|
||||
Copyright (C) 2017 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.
|
||||
-->
|
||||
|
||||
<link rel="import" href="../../../bower_components/polymer/polymer.html">
|
||||
<link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
|
||||
<link rel="import" href="gr-plugin-project-command.html">
|
||||
|
||||
<dom-module id="gr-project-api">
|
||||
<script src="gr-project-api.js"></script>
|
||||
</dom-module>
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright (C) 2017 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.
|
||||
(function(window) {
|
||||
'use strict';
|
||||
|
||||
// Prevent redefinition.
|
||||
if (window.GrProjectApi) { return; }
|
||||
|
||||
function GrProjectApi(plugin) {
|
||||
this._hook = null;
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
GrProjectApi.prototype._createHook = function(title) {
|
||||
this._hook = this.plugin.hook('project-command').onAttached(element => {
|
||||
const pluginCommand =
|
||||
document.createElement('gr-plugin-project-command');
|
||||
pluginCommand.title = title;
|
||||
element.appendChild(pluginCommand);
|
||||
});
|
||||
};
|
||||
|
||||
GrProjectApi.prototype.createCommand = function(title, callback) {
|
||||
if (this._hook) {
|
||||
console.warn('Already set up.');
|
||||
return this._hook;
|
||||
}
|
||||
this._createHook(title);
|
||||
this._hook.onAttached(element => {
|
||||
if (callback(element.projectName, element.config) === false) {
|
||||
element.hidden = true;
|
||||
}
|
||||
});
|
||||
return this;
|
||||
};
|
||||
|
||||
GrProjectApi.prototype.onTap = function(callback) {
|
||||
if (!this._hook) {
|
||||
console.warn('Call createCommand first.');
|
||||
return this;
|
||||
}
|
||||
this._hook.onAttached(element => {
|
||||
this.plugin.eventHelper(element).on('command-tap', callback);
|
||||
});
|
||||
return this;
|
||||
};
|
||||
|
||||
window.GrProjectApi = GrProjectApi;
|
||||
})(window);
|
||||
@@ -0,0 +1,80 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
Copyright (C) 2017 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.
|
||||
-->
|
||||
|
||||
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
|
||||
<title>gr-project-api</title>
|
||||
|
||||
<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
|
||||
<script src="../../../bower_components/web-component-tester/browser.js"></script>
|
||||
<link rel="import" href="../../../test/common-test-setup.html"/>
|
||||
<link rel="import" href="../gr-endpoint-decorator/gr-endpoint-decorator.html">
|
||||
<link rel="import" href="gr-project-api.html">
|
||||
|
||||
<script>void(0);</script>
|
||||
|
||||
<test-fixture id="basic">
|
||||
<template>
|
||||
<gr-endpoint-decorator name="project-command">
|
||||
</gr-endpoint-decorator>
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
<script>
|
||||
suite('gr-project-api tests', () => {
|
||||
let sandbox;
|
||||
let projectApi;
|
||||
|
||||
setup(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
let plugin;
|
||||
Gerrit.install(p => { plugin = p; }, '0.1',
|
||||
'http://test.com/plugins/testplugin/static/test.js');
|
||||
sandbox.stub(Gerrit, '_arePluginsLoaded').returns(true);
|
||||
projectApi = plugin.project();
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
projectApi = null;
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
test('exists', () => {
|
||||
assert.isOk(projectApi);
|
||||
});
|
||||
|
||||
test('works', done => {
|
||||
const attachedStub = sandbox.stub();
|
||||
const tapStub = sandbox.stub();
|
||||
projectApi
|
||||
.createCommand('foo', attachedStub)
|
||||
.onTap(tapStub);
|
||||
const element = fixture('basic');
|
||||
flush(() => {
|
||||
assert.isTrue(attachedStub.called);
|
||||
const pluginCommand = element.$$('gr-plugin-project-command');
|
||||
assert.isOk(pluginCommand);
|
||||
const command = pluginCommand.$$('gr-project-command');
|
||||
assert.isOk(command);
|
||||
assert.equal(command.title, 'foo');
|
||||
assert.isFalse(tapStub.called);
|
||||
MockInteractions.tap(command.$$('gr-button'));
|
||||
assert.isTrue(tapStub.called);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -21,6 +21,7 @@ limitations under the License.
|
||||
<link rel="import" href="../../plugins/gr-dom-hooks/gr-dom-hooks.html">
|
||||
<link rel="import" href="../../plugins/gr-event-helper/gr-event-helper.html">
|
||||
<link rel="import" href="../../plugins/gr-popup-interface/gr-popup-interface.html">
|
||||
<link rel="import" href="../../plugins/gr-project-api/gr-project-api.html">
|
||||
<link rel="import" href="../../plugins/gr-theme-api/gr-theme-api.html">
|
||||
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
|
||||
<link rel="import" href="../gr-rest-api-interface/gr-rest-api-interface.html">
|
||||
|
||||
@@ -198,6 +198,10 @@
|
||||
return new GrThemeApi(this);
|
||||
};
|
||||
|
||||
Plugin.prototype.project = function() {
|
||||
return new GrProjectApi(this);
|
||||
};
|
||||
|
||||
Plugin.prototype.attributeHelper = function(element) {
|
||||
return new GrAttributeHelper(element);
|
||||
};
|
||||
|
||||
42
polygerrit-ui/app/samples/project-command.html
Normal file
42
polygerrit-ui/app/samples/project-command.html
Normal file
@@ -0,0 +1,42 @@
|
||||
<dom-module id="sample-project-command">
|
||||
<script>
|
||||
Gerrit.install(plugin => {
|
||||
// High-level API
|
||||
plugin.project()
|
||||
.createCommand('Bork', (projectName, projectConfig) => {
|
||||
if (projectName !== 'All-Projects') {
|
||||
return false;
|
||||
}
|
||||
}).onTap(() => {
|
||||
alert('Bork, bork!');
|
||||
});
|
||||
|
||||
// Low-level API
|
||||
plugin.registerCustomComponent(
|
||||
'project-command', 'project-command-low');
|
||||
});
|
||||
</script>
|
||||
</dom-module>
|
||||
|
||||
<!-- Low-level custom component for project command. -->
|
||||
<dom-module id="project-command-low">
|
||||
<template>
|
||||
<gr-project-command
|
||||
title="Low-level bork"
|
||||
on-command-tap="_handleCommandTap">
|
||||
</gr-project-command>
|
||||
</template>
|
||||
<script>
|
||||
Polymer({
|
||||
is: 'project-command-low',
|
||||
attached() {
|
||||
console.log(this.projectName);
|
||||
console.log(this.config);
|
||||
this.hidden = this.projectName !== 'All-Projects';
|
||||
},
|
||||
_handleCommandTap() {
|
||||
alert('(softly) bork, bork.');
|
||||
},
|
||||
});
|
||||
</script>
|
||||
</dom-module>
|
||||
@@ -44,6 +44,7 @@ limitations under the License.
|
||||
'admin/gr-permission/gr-permission_test.html',
|
||||
'admin/gr-plugin-list/gr-plugin-list_test.html',
|
||||
'admin/gr-project-access/gr-project-access_test.html',
|
||||
'admin/gr-project-command/gr-project-command_test.html',
|
||||
'admin/gr-project-commands/gr-project-commands_test.html',
|
||||
'admin/gr-project-detail-list/gr-project-detail-list_test.html',
|
||||
'admin/gr-project-list/gr-project-list_test.html',
|
||||
@@ -107,6 +108,7 @@ limitations under the License.
|
||||
'plugins/gr-endpoint-decorator/gr-endpoint-decorator_test.html',
|
||||
'plugins/gr-event-helper/gr-event-helper_test.html',
|
||||
'plugins/gr-external-style/gr-external-style_test.html',
|
||||
'plugins/gr-project-api/gr-project-api_test.html',
|
||||
'plugins/gr-plugin-host/gr-plugin-host_test.html',
|
||||
'plugins/gr-popup-interface/gr-plugin-popup_test.html',
|
||||
'plugins/gr-popup-interface/gr-popup-interface_test.html',
|
||||
|
||||
Reference in New Issue
Block a user