Add gr-repo-plugin-config
Element is used to edit the plugin-specific config of a repository.
Bug: Issue 8535
Change-Id: Ib2962da67f5f89a86f3da652d48382f51ca1e81b
diff --git a/polygerrit-ui/app/behaviors/gr-repo-plugin-config-behavior/gr-repo-plugin-config-behavior.html b/polygerrit-ui/app/behaviors/gr-repo-plugin-config-behavior/gr-repo-plugin-config-behavior.html
new file mode 100644
index 0000000..2dc070d
--- /dev/null
+++ b/polygerrit-ui/app/behaviors/gr-repo-plugin-config-behavior/gr-repo-plugin-config-behavior.html
@@ -0,0 +1,38 @@
+<!--
+@license
+Copyright (C) 2019 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.
+-->
+<script>
+(function(window) {
+ 'use strict';
+
+ window.Gerrit = window.Gerrit || {};
+
+ /** @polymerBehavior this */
+ Gerrit.RepoPluginConfig = {
+ // Should be kept in sync with
+ // gerrit/java/com/google/gerrit/extensions/api/projects/ProjectConfigEntryType.java.
+ ENTRY_TYPES: {
+ ARRAY: 'ARRAY',
+ BOOLEAN: 'BOOLEAN',
+ INT: 'INT',
+ LIST: 'LIST',
+ LONG: 'LONG',
+ STRING: 'STRING',
+ },
+ PLUGIN_CONFIG_CHANGED: 'plugin-config-changed',
+ };
+})(window);
+</script>
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config.html b/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config.html
new file mode 100644
index 0000000..7f2cbe7
--- /dev/null
+++ b/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config.html
@@ -0,0 +1,109 @@
+<!--
+@license
+Copyright (C) 2018 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="../../../bower_components/iron-input/iron-input.html">
+<link rel="import" href="../../../bower_components/paper-toggle-button/paper-toggle-button.html">
+
+<link rel="import" href="../../../behaviors/gr-repo-plugin-config-behavior/gr-repo-plugin-config-behavior.html">
+<link rel="import" href="../../../styles/gr-form-styles.html">
+<link rel="import" href="../../../styles/gr-subpage-styles.html">
+<link rel="import" href="../../../styles/shared-styles.html">
+<link rel="import" href="../../shared/gr-icons/gr-icons.html">
+<link rel="import" href="../../shared/gr-select/gr-select.html">
+<link rel="import" href="../../shared/gr-tooltip-content/gr-tooltip-content.html">
+<link rel="import" href="../gr-plugin-config-array-editor/gr-plugin-config-array-editor.html">
+
+<dom-module id="gr-repo-plugin-config">
+ <template>
+ <style include="shared-styles"></style>
+ <style include="gr-form-styles"></style>
+ <style include="gr-subpage-styles">
+ .inherited {
+ color: var(--deemphasized-text-color);
+ margin-left: .5em;
+ }
+ section.section:not(.ARRAY) .title {
+ align-items: center;
+ display: flex;
+ }
+ section.section.ARRAY .title {
+ padding-top: .75em;
+ }
+ </style>
+ <div class="gr-form-styles">
+ <fieldset>
+ <h4>[[pluginData.name]]</h4>
+ <template is="dom-repeat" items="[[_pluginConfigOptions]]" as="option">
+ <section class$="section [[option.info.type]]">
+ <span class="title">
+ <gr-tooltip-content
+ has-tooltip="[[option.info.description]]"
+ show-icon="[[option.info.description]]"
+ title="[[option.info.description]]">
+ <span>[[option.info.display_name]]</span>
+ </gr-tooltip-content>
+ </span>
+ <span class="value">
+ <template is="dom-if" if="[[_isArray(option.info.type)]]">
+ <gr-plugin-config-array-editor
+ on-plugin-config-option-changed="_handleArrayChange"
+ plugin-option="[[option]]"></gr-plugin-config-array-editor>
+ </template>
+ <template is="dom-if" if="[[_isBoolean(option.info.type)]]">
+ <paper-toggle-button
+ checked="[[_computeChecked(option.info.value)]]"
+ on-change="_handleBooleanChange"
+ data-option-key$="[[option._key]]"
+ disabled$="[[_computeDisabled(option.info.editable)]]"></paper-toggle-button>
+ </template>
+ <template is="dom-if" if="[[_isList(option.info.type)]]">
+ <gr-select
+ bind-value$="[[option.info.value]]"
+ on-change="_handleListChange">
+ <select
+ data-option-key$="[[option._key]]"
+ disabled$="[[_computeDisabled(option.info.editable)]]">
+ <template is="dom-repeat"
+ items="[[option.info.permitted_values]]"
+ as="value">
+ <option value$="[[value]]">[[value]]</option>
+ </template>
+ </select>
+ </gr-select>
+ </template>
+ <template is="dom-if" if="[[_isString(option.info.type)]]">
+ <input
+ is="iron-input"
+ value="[[option.info.value]]"
+ on-input="_handleStringChange"
+ data-option-key$="[[option._key]]"
+ disabled$="[[_computeDisabled(option.info.editable)]]"></input>
+ </template>
+ <template is="dom-if" if="[[option.info.inherited_value]]">
+ <span class="inherited">
+ (Inherited: [[option.info.inherited_value]])
+ </span>
+ </template>
+ </span>
+ </section>
+ </template>
+ </fieldset>
+ </div>
+ </template>
+ <script src="gr-repo-plugin-config.js"></script>
+</dom-module>
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config.js b/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config.js
new file mode 100644
index 0000000..6d7677e
--- /dev/null
+++ b/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config.js
@@ -0,0 +1,130 @@
+/**
+ * @license
+ * Copyright (C) 2018 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-repo-plugin-config',
+
+ /**
+ * Fired when the plugin config changes.
+ *
+ * @event plugin-config-changed
+ */
+
+ properties: {
+ /** @type {?} */
+ pluginData: Object,
+ /** @type {Array} */
+ _pluginConfigOptions: {
+ type: Array,
+ computed: '_computePluginConfigOptions(pluginData.*)',
+ },
+ },
+
+ behaviors: [
+ Gerrit.RepoPluginConfig,
+ ],
+
+ _computePluginConfigOptions(dataRecord) {
+ if (!dataRecord || !dataRecord.base || !dataRecord.base.config) {
+ return [];
+ }
+ const {config} = dataRecord.base;
+ return Object.keys(config).map(_key => ({_key, info: config[_key]}));
+ },
+
+ _isArray(type) {
+ return type === this.ENTRY_TYPES.ARRAY;
+ },
+
+ _isBoolean(type) {
+ return type === this.ENTRY_TYPES.BOOLEAN;
+ },
+
+ _isList(type) {
+ return type === this.ENTRY_TYPES.LIST;
+ },
+
+ _isString(type) {
+ // Treat numbers like strings for simplicity.
+ return type === this.ENTRY_TYPES.STRING ||
+ type === this.ENTRY_TYPES.INT ||
+ type === this.ENTRY_TYPES.LONG;
+ },
+
+ _computeDisabled(editable) {
+ return editable === 'false';
+ },
+
+ _computeChecked(value) {
+ return JSON.parse(value);
+ },
+
+ _handleStringChange(e) {
+ const el = Polymer.dom(e).localTarget;
+ const _key = el.getAttribute('data-option-key');
+ const configChangeInfo =
+ this._buildConfigChangeInfo(el.value, _key);
+ this._handleChange(configChangeInfo);
+ },
+
+ _handleListChange(e) {
+ const el = Polymer.dom(e).localTarget;
+ const _key = el.getAttribute('data-option-key');
+ const configChangeInfo =
+ this._buildConfigChangeInfo(el.value, _key);
+ this._handleChange(configChangeInfo);
+ },
+
+ _handleBooleanChange(e) {
+ const el = Polymer.dom(e).localTarget;
+ const _key = el.getAttribute('data-option-key');
+ const configChangeInfo =
+ this._buildConfigChangeInfo(JSON.stringify(el.checked), _key);
+ this._handleChange(configChangeInfo);
+ },
+
+ _buildConfigChangeInfo(value, _key) {
+ const info = this.pluginData.config[_key];
+ info.value = value;
+ return {
+ _key,
+ info,
+ notifyPath: `${_key}.value`,
+ };
+ },
+
+ _handleArrayChange({detail}) {
+ this._handleChange(detail);
+ },
+
+ _handleChange({_key, info, notifyPath}) {
+ const {name, config} = this.pluginData;
+
+ /** @type {Object} */
+ const detail = {
+ name,
+ config: Object.assign(config, {[_key]: info}, {}),
+ notifyPath: `${name}.${notifyPath}`,
+ };
+
+ this.dispatchEvent(new CustomEvent(this.PLUGIN_CONFIG_CHANGED,
+ {detail, bubbles: true}));
+ },
+ });
+})();
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config_test.html b/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config_test.html
new file mode 100644
index 0000000..2af2043
--- /dev/null
+++ b/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config_test.html
@@ -0,0 +1,175 @@
+<!DOCTYPE html>
+<!--
+@license
+Copyright (C) 2018 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-repo-plugin-config</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-repo-plugin-config.html">
+
+<script>void(0);</script>
+
+<test-fixture id="basic">
+ <template>
+ <gr-repo-plugin-config></gr-repo-plugin-config>
+ </template>
+</test-fixture>
+
+<script>
+ suite('gr-repo-plugin-config tests', () => {
+ let element;
+ let sandbox;
+
+ setup(() => {
+ sandbox = sinon.sandbox.create();
+ element = fixture('basic');
+ });
+
+ teardown(() => sandbox.restore());
+
+ test('_computePluginConfigOptions', () => {
+ assert.deepEqual(element._computePluginConfigOptions(), []);
+ assert.deepEqual(element._computePluginConfigOptions({}), []);
+ assert.deepEqual(element._computePluginConfigOptions({base: {}}), []);
+ assert.deepEqual(element._computePluginConfigOptions(
+ {base: {config: {}}}), []);
+ assert.deepEqual(element._computePluginConfigOptions(
+ {base: {config: {testKey: 'testInfo'}}}),
+ [{_key: 'testKey', info: 'testInfo'}]);
+ });
+
+ test('_computeDisabled', () => {
+ assert.isFalse(element._computeDisabled('true'));
+ assert.isTrue(element._computeDisabled('false'));
+ });
+
+ test('_handleChange', () => {
+ const eventStub = sandbox.stub(element, 'dispatchEvent');
+ element.pluginData = {
+ name: 'testName',
+ config: {plugin: {value: 'test'}},
+ };
+ element._handleChange({
+ _key: 'plugin',
+ info: {value: 'newTest'},
+ notifyPath: 'plugin.value',
+ });
+
+ assert.isTrue(eventStub.called);
+
+ const {detail} = eventStub.lastCall.args[0];
+ assert.equal(detail.name, 'testName');
+ assert.deepEqual(detail.config, {plugin: {value: 'newTest'}});
+ assert.equal(detail.notifyPath, 'testName.plugin.value');
+ });
+
+ suite('option types', () => {
+ let changeStub;
+ let buildStub;
+
+ setup(() => {
+ changeStub = sandbox.stub(element, '_handleChange');
+ buildStub = sandbox.stub(element, '_buildConfigChangeInfo');
+ });
+
+ test('ARRAY type option', () => {
+ element.pluginData = {
+ name: 'testName',
+ config: {plugin: {value: 'test', type: 'ARRAY'}},
+ };
+ flushAsynchronousOperations();
+
+ const editor = element.$$('gr-plugin-config-array-editor');
+ assert.ok(editor);
+ element._handleArrayChange({detail: 'test'});
+ assert.isTrue(changeStub.called);
+ assert.equal(changeStub.lastCall.args[0], 'test');
+ });
+
+ test('BOOLEAN type option', () => {
+ element.pluginData = {
+ name: 'testName',
+ config: {plugin: {value: 'true', type: 'BOOLEAN'}},
+ };
+ flushAsynchronousOperations();
+
+ const toggle = element.$$('paper-toggle-button');
+ assert.ok(toggle);
+ toggle.click();
+ flushAsynchronousOperations();
+
+ assert.isTrue(buildStub.called);
+ assert.deepEqual(buildStub.lastCall.args, ['false', 'plugin']);
+
+ assert.isTrue(changeStub.called);
+ });
+
+ test('INT/LONG/STRING type option', () => {
+ element.pluginData = {
+ name: 'testName',
+ config: {plugin: {value: 'test', type: 'STRING'}},
+ };
+ flushAsynchronousOperations();
+
+ const input = element.$$('input');
+ assert.ok(input);
+ input.value = 'newTest';
+ input.dispatchEvent(new Event('input'));
+ flushAsynchronousOperations();
+
+ assert.isTrue(buildStub.called);
+ assert.deepEqual(buildStub.lastCall.args, ['newTest', 'plugin']);
+
+ assert.isTrue(changeStub.called);
+ });
+
+ test('LIST type option', () => {
+ const permitted_values = ['test', 'newTest'];
+ element.pluginData = {
+ name: 'testName',
+ config: {plugin: {value: 'test', type: 'LIST', permitted_values}},
+ };
+ flushAsynchronousOperations();
+
+ const select = element.$$('select');
+ assert.ok(select);
+ select.value = 'newTest';
+ select.dispatchEvent(new Event('change', {bubbles: true}));
+ flushAsynchronousOperations();
+
+ assert.isTrue(buildStub.called);
+ assert.deepEqual(buildStub.lastCall.args, ['newTest', 'plugin']);
+
+ assert.isTrue(changeStub.called);
+ });
+ });
+
+ test('_buildConfigChangeInfo', () => {
+ element.pluginData = {
+ name: 'testName',
+ config: {plugin: {value: 'test'}},
+ };
+ const detail = element._buildConfigChangeInfo('newTest', 'plugin');
+ assert.equal(detail._key, 'plugin');
+ assert.deepEqual(detail.info, {value: 'newTest'});
+ assert.equal(detail.notifyPath, 'plugin.value');
+ });
+ });
+</script>
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.html b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.html
index 21bf649..538bab7 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.html
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.html
@@ -55,6 +55,9 @@
#email {
margin-bottom: 1em;
}
+ main section.darkToggle {
+ display: block;
+ }
.filters p,
.darkToggle p {
margin-bottom: 1em;
diff --git a/polygerrit-ui/app/styles/gr-form-styles.html b/polygerrit-ui/app/styles/gr-form-styles.html
index 59b633f..65c1ae3 100644
--- a/polygerrit-ui/app/styles/gr-form-styles.html
+++ b/polygerrit-ui/app/styles/gr-form-styles.html
@@ -29,11 +29,15 @@
.gr-form-styles h2 {
margin-bottom: .3em;
}
+ .gr-form-styles h4 {
+ font-weight: var(--font-weight-bold);
+ }
.gr-form-styles fieldset {
border: none;
margin-bottom: 2em;
}
.gr-form-styles section {
+ display: flex;
margin: .25em 0;
min-height: 2em;
}
diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html
index 395df6d..bc705a8 100644
--- a/polygerrit-ui/app/test/index.html
+++ b/polygerrit-ui/app/test/index.html
@@ -52,6 +52,7 @@
'admin/gr-repo-dashboards/gr-repo-dashboards_test.html',
'admin/gr-repo-detail-list/gr-repo-detail-list_test.html',
'admin/gr-repo-list/gr-repo-list_test.html',
+ 'admin/gr-repo-plugin-config/gr-repo-plugin-config_test.html',
'admin/gr-repo/gr-repo_test.html',
'admin/gr-rule-editor/gr-rule-editor_test.html',
'change-list/gr-change-list-item/gr-change-list-item_test.html',