Support for .settingsScreen() plugin API

Change-Id: I05ef82957952893ca64c5af5ef59d2c0a54d95c1
This commit is contained in:
Viktar Donich 2017-12-21 15:46:02 -08:00
parent 9a0fa9f65a
commit 17c02269fc
14 changed files with 432 additions and 4 deletions

View File

@ -311,6 +311,21 @@ screen.
.Returns:
- Absolute URL for the screen, e.g. `http://localhost/base/x/pluginname/screenname`
[[plugin-settings]]
=== settings
`plugin.settings()`
.Params:
- none
.Returns:
- Instance of link:pg-plugin-settings-api.html[GrSettingsApi].
=== settingsScreen
`plugin.settingsScreen(path, menu, callback)`
Deprecated. Use link:#plugin-settings[`plugin.settings()`] instead.
=== theme
`plugin.theme()`
@ -374,6 +389,19 @@ Support is limited:
See link:js-api.html#self_panel[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 link:js-api.html#self_settingsScreen[self.settingsScreen] for more info.
[[deprecated-action-context]]
=== Action Context (deprecated)
Instance of Action Context is passed to `onAction()` callback.

View File

@ -0,0 +1,40 @@
= Gerrit Code Review - Settings admin customization API
This API is provided by link:pg-plugin-dev.html#plugin-settings[plugin.settings()]
and provides customization to settings page.
== title
`settingsApi.title(title)`
.Params
- `*string* title` Menu item and settings section title
.Returns
- `GrSettingsApi` for chaining.
== token
`settingsApi.token(token)`
.Params
- `*string* token` URL path fragment of the screen for direct link, e.g.
`settings/#x/some-plugin/*token*`
.Returns
- `GrSettingsApi` for chaining.
== module
`settingsApi.module(token)`
.Params
- `*string* module` Custom element name for instantiating in the settings plugin
area.
.Returns
- `GrSettingsApi` for chaining.
== build
.Params
- none
Apply all other configuration parameters and create required UI elements.

View File

@ -0,0 +1,26 @@
<!--
Copyright (C) 2017 The Android Open Source Settings
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="../../../behaviors/base-url-behavior/base-url-behavior.html">
<link rel="import" href="../../settings/gr-settings-view/gr-settings-item.html">
<link rel="import" href="../../settings/gr-settings-view/gr-settings-menu-item.html">
<link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
<link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
<dom-module id="gr-settings-api">
<script src="gr-settings-api.js"></script>
</dom-module>

View File

@ -0,0 +1,62 @@
// Copyright (C) 2017 The Android Open Source Settings
//
// 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';
function GrSettingsApi(plugin) {
this._title = '(no title)';
// Generate default screen URL token, specific to plugin, and unique(ish).
this._token =
plugin.getPluginName() + Math.random().toString(36).substr(5);
this.plugin = plugin;
}
GrSettingsApi.prototype.title = function(title) {
this._title = title;
return this;
};
GrSettingsApi.prototype.token = function(token) {
this._token = token;
return this;
};
GrSettingsApi.prototype.module = function(moduleName) {
this._moduleName = moduleName;
return this;
};
GrSettingsApi.prototype.build = function() {
if (!this._moduleName) {
throw new Error('Settings screen custom element not defined!');
}
const token = `x/${this.plugin.getPluginName()}/${this._token}`;
this.plugin.hook('settings-menu-item').onAttached(el => {
const menuItem = document.createElement('gr-settings-menu-item');
menuItem.title = this._title;
menuItem.href = `#${token}`;
el.appendChild(menuItem);
});
return this.plugin.hook('settings-screen').onAttached(el => {
const item = document.createElement('gr-settings-item');
item.title = this._title;
item.anchor = token;
item.appendChild(document.createElement(this._moduleName));
el.appendChild(item);
});
};
window.GrSettingsApi = GrSettingsApi;
})(window);

View File

@ -0,0 +1,82 @@
<!DOCTYPE html>
<!--
Copyright (C) 2017 The Android Open Source Settings
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-settings-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-settings-api.html">
<script>void(0);</script>
<test-fixture id="basic">
<template>
<gr-endpoint-decorator name="settings-menu-item">
</gr-endpoint-decorator>
<gr-endpoint-decorator name="settings-screen">
</gr-endpoint-decorator>
</template>
</test-fixture>
<script>
suite('gr-settings-api tests', () => {
let sandbox;
let settingsApi;
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);
settingsApi = plugin.settings();
});
teardown(() => {
settingsApi = null;
sandbox.restore();
});
test('exists', () => {
assert.isOk(settingsApi);
});
test('works', done => {
settingsApi
.title('foo')
.token('bar')
.module('some-settings-screen')
.build();
const element = fixture('basic');
flush(() => {
const [menuItemEl, itemEl] = element;
const menuItem = menuItemEl.$$('gr-settings-menu-item');
assert.isOk(menuItem);
assert.equal(menuItem.title, 'foo');
assert.equal(menuItem.href, '#x/testplugin/bar');
const item = itemEl.$$('gr-settings-item');
assert.isOk(item);
assert.equal(item.title, 'foo');
assert.equal(item.anchor, 'x/testplugin/bar');
done();
});
});
});
</script>

View File

@ -0,0 +1,31 @@
<!--
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">
<dom-module id="gr-settings-item">
<style>
:host {
display: block;
margin-bottom: 2em;
}
</style>
<template>
<h2 id="[[anchor]]">[[title]]</h2>
<slot></slot>
</template>
<script src="gr-settings-item.js"></script>
</dom-module>

View File

@ -0,0 +1,24 @@
// 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-settings-item',
properties: {
anchor: String,
title: String,
},
});
})();

View File

@ -0,0 +1,29 @@
<!--
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/gr-page-nav-styles.html">
<dom-module id="gr-settings-menu-item">
<style include="shared-styles"></style>
<style include="gr-page-nav-styles"></style>
<template>
<div class="navStyles">
<li><a href$="[[href]]">[[title]]</a></li>
</div>
</template>
<script src="gr-settings-menu-item.js"></script>
</dom-module>

View File

@ -0,0 +1,24 @@
// 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-settings-menu-item',
properties: {
href: String,
title: String,
},
});
})();

View File

@ -15,12 +15,12 @@ limitations under the License.
-->
<link rel="import" href="../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/docs-url-behavior/docs-url-behavior.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../../styles/gr-form-styles.html">
<link rel="import" href="../../../styles/gr-menu-page-styles.html">
<link rel="import" href="../../../styles/gr-page-nav-styles.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="../../settings/gr-change-table-editor/gr-change-table-editor.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
<link rel="import" href="../../shared/gr-date-formatter/gr-date-formatter.html">
@ -79,6 +79,8 @@ limitations under the License.
</li>
</template>
<li><a href="#MailFilters">Mail Filters</a></li>
<gr-endpoint-decorator name="settings-menu-item">
</gr-endpoint-decorator>
</ul>
</gr-page-nav>
<main class="gr-form-styles">
@ -480,6 +482,8 @@ limitations under the License.
</tbody>
</table>
</fieldset>
<gr-endpoint-decorator name="settings-screen">
</gr-endpoint-decorator>
</main>
</div>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>

View File

@ -22,6 +22,7 @@ limitations under the License.
<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-repo-api/gr-repo-api.html">
<link rel="import" href="../../plugins/gr-settings-api/gr-settings-api.html">
<link rel="import" href="../../plugins/gr-theme-api/gr-theme-api.html">
<link rel="import" href="../gr-rest-api-interface/gr-rest-api-interface.html">

View File

@ -518,5 +518,49 @@ limitations under the License.
});
});
});
suite('settingsScreen', () => {
test('plugin.settingsScreen is deprecated', () => {
plugin.settingsScreen('rubbish');
assert.isTrue(console.error.called);
});
test('plugin.settings() returns GrSettingsApi', () => {
assert.isOk(plugin.settings());
assert.isTrue(plugin.settings() instanceof GrSettingsApi);
});
test('plugin.deprecated.settingsScreen() works', () => {
const hookStub = {onAttached: sandbox.stub()};
sandbox.stub(plugin, 'hook').returns(hookStub);
const fakeSettings = {};
fakeSettings.title = sandbox.stub().returns(fakeSettings);
fakeSettings.token = sandbox.stub().returns(fakeSettings);
fakeSettings.module = sandbox.stub().returns(fakeSettings);
fakeSettings.build = sandbox.stub().returns(hookStub);
sandbox.stub(plugin, 'settings').returns(fakeSettings);
const callback = sandbox.stub();
plugin.deprecated.settingsScreen('path', 'menu', callback);
assert.isTrue(fakeSettings.title.calledWith('menu'));
assert.isTrue(fakeSettings.token.calledWith('path'));
assert.isTrue(fakeSettings.module.calledWith('div'));
assert.equal(fakeSettings.build.callCount, 1);
const fakeBody = {};
const fakeEl = {
style: {
display: '',
},
querySelector: sandbox.stub().returns(fakeBody),
};
// Emulate settings screen attached
hookStub.onAttached.callArgWith(0, fakeEl);
assert.isTrue(callback.called);
const args = callback.args[0][0];
assert.strictEqual(args.body, fakeBody);
assert.equal(fakeEl.style.display, 'none');
});
});
});
</script>

View File

@ -23,7 +23,7 @@
*/
const plugins = {};
const stubbedMethods = ['_loadedGwt', 'settingsScreen'];
const stubbedMethods = ['_loadedGwt'];
const GWT_PLUGIN_STUB = {};
for (const name of stubbedMethods) {
GWT_PLUGIN_STUB[name] = warnNotSupported.bind(null, name);
@ -118,6 +118,7 @@
panel: deprecatedAPI.panel.bind(this),
popup: deprecatedAPI.popup.bind(this),
screen: deprecatedAPI.screen.bind(this),
settingsScreen: deprecatedAPI.settingsScreen.bind(this),
};
this._url = new URL(opt_url);
@ -228,6 +229,10 @@
return new GrRepoApi(this);
};
Plugin.prototype.settings = function() {
return new GrSettingsApi(this);
};
/**
* To make REST requests for plugin-provided endpoints, use
* @example
@ -260,6 +265,11 @@
'Use registerCustomComponent() instead.');
};
Plugin.prototype.settingsScreen = function() {
console.error('.settingsScreen() is deprecated! ' +
'Use .settings() instead.');
};
Plugin.prototype.screen = function(screenName, opt_moduleName) {
if (opt_moduleName && typeof opt_moduleName !== 'string') {
throw new Error('deprecated, use deprecated.screen');
@ -333,6 +343,28 @@
});
},
settingsScreen(path, menu, callback) {
console.warn('.settingsScreen() is deprecated! Use .settings() instead.');
const hook = this.settings()
.title(menu)
.token(path)
.module('div')
.build();
hook.onAttached(el => {
el.style.display = 'none';
const body = el.querySelector('div');
callback({
body,
onUnload: () => {},
setTitle: () => {},
setWindowTitle: () => {},
show: () => {
el.style.display = 'initial';
},
});
});
},
panel(extensionpoint, callback) {
console.warn('.panel() is deprecated! ' +
'Use registerCustomComponent() instead.');

View File

@ -116,6 +116,7 @@ limitations under the License.
'plugins/gr-popup-interface/gr-plugin-popup_test.html',
'plugins/gr-popup-interface/gr-popup-interface_test.html',
'plugins/gr-repo-api/gr-repo-api_test.html',
'plugins/gr-settings-api/gr-settings-api_test.html',
'settings/gr-account-info/gr-account-info_test.html',
'settings/gr-change-table-editor/gr-change-table-editor_test.html',
'settings/gr-email-editor/gr-email-editor_test.html',