Merge "Add download commands to project pages"

This commit is contained in:
Viktar Donich
2017-06-09 21:52:03 +00:00
committed by Gerrit Code Review
3 changed files with 277 additions and 164 deletions

View File

@@ -18,183 +18,207 @@ limitations under the License.
<link rel="import" href="../../../bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
<link rel="import" href="../../shared/gr-download-commands/gr-download-commands.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
<link rel="import" href="../../shared/gr-select/gr-select.html">
<link rel="import" href="../../../styles/gr-form-styles.html">
<link rel="import" href="../../../styles/shared-styles.html">
<dom-module id="gr-admin-project">
<template>
<style include="shared-styles">
main {
margin: 2em 1em;
}
h1.edited:after {
h2.edited:after {
color: #444;
content: ' *';
}
.loading,
.hideDownload {
display: none;
}
#loading.loading {
display: block;
}
#loading:not(.loading) {
display: none;
}
</style>
<style include="gr-form-styles"></style>
<main class="gr-form-styles read-only">
<h1 id="Title" class$="[[_computeHeaderClass(_configChanged)]]">
[[project]]</h1>
<div id="loading" hidden$="[[!_loading]]" hidden>Loading...</div>
<div id="form" hidden$="[[_loading]]" hidden>
<h2 id="Description">Description</h2>
<fieldset>
<iron-autogrow-textarea
id="descriptionInput"
class="description"
autocomplete="on"
placeholder="<Insert project description here>"
bind-value="{{_projectConfig.description}}"
disabled$="[[_readOnly]]"></iron-autogrow-textarea>
</fieldset>
<h2 id="Options">Project Options</h2>
<fieldset id="options">
<section>
<span class="title">State</span>
<span class="value">
<select
id="stateSelect"
is="gr-select"
bind-value="{{_projectConfig.state}}"
disabled$="[[_readOnly]]">
<template is="dom-repeat" items=[[_states]]>
<option value="[[item.value]]">[[item.label]]</option>
</template>
</select>
</span>
</section>
<section>
<span class="title">Submit type</span>
<span class="value">
<select
id="submitTypeSelect"
is="gr-select"
bind-value="{{_projectConfig.submit_type}}"
disabled$="[[_readOnly]]">>
<template is="dom-repeat" items="[[_submitTypes]]">
<option value="[[item.value]]">[[item.label]]</option>
</template>
</select>
</span>
</section>
<section>
<span class="title">Allow content merges</span>
<span class="value">
<select
id="contentMergeSelect"
is="gr-select"
bind-value="{{_projectConfig.use_content_merge.configured_value}}"
disabled$="[[_readOnly]]">>
<template is="dom-repeat"
items="[[_formatBooleanSelect(_projectConfig.use_content_merge)]]">
<option value="[[item.value]]">[[item.label]]</option>
</template>
</select>
</span>
</section>
<section>
<span class="title">
Create a new change for every commit not in the target branch
</span>
<span class="value">
<select
id="newChangeSelect"
is="gr-select"
bind-value="{{_projectConfig.create_new_change_for_all_not_in_target.configured_value}}"
disabled$="[[_readOnly]]">>
<template is="dom-repeat"
items="[[_formatBooleanSelect(_projectConfig.create_new_change_for_all_not_in_target)]]">
<option value="[[item.value]]">[[item.label]]</option>
</template>
</select>
</span>
</section>
<section>
<span class="title">Require Change-Id in commit message</span>
<span class="value">
<select
id="requireChangeIdSelect"
is="gr-select"
bind-value="{{_projectConfig.require_change_id.configured_value}}"
disabled$="[[_readOnly]]">>
<template is="dom-repeat"
items="[[_formatBooleanSelect(_projectConfig.require_change_id)]]">
<option value="[[item.value]]">[[item.label]]</option>
</template>
</select>
</span>
</section>
<section>
<span class="title">
Reject implicit merges when changes are pushed for review</span>
<span class="value">
<select
id="rejectImplicitMergesSelect"
is="gr-select"
bind-value="{{_projectConfig.reject_implicit_merges.configured_value}}"
disabled$="[[_readOnly]]">>
<template is="dom-repeat"
items="[[_formatBooleanSelect(_projectConfig.reject_implicit_merges)]]">
<option value="[[item.value]]">[[item.label]]</option>
</template>
</select>
</span>
</section>
<section>
<span class="title">Maximum Git object size limit</span>
<span class="value">
<input
id="maxGitObjSizeInput"
bind-value="{{_projectConfig.max_object_size_limit.configured_value}}"
is="iron-input"
type="text"
disabled$="[[_readOnly]]">
</span>
</section>
</fieldset>
<h2 id="Options">Contributor Agreements</h2>
<fieldset id="agreements">
<section>
<span class="title">
Require a valid contributor agreement to upload</span>
<span class="value">
<select
id="contributorAgreementSelect"
is="gr-select"
bind-value="{{_projectConfig.use_contributor_agreements.configured_value}}"
disabled$="[[_readOnly]]">>
<template is="dom-repeat"
items="[[_formatBooleanSelect(_projectConfig.use_contributor_agreements)]]">
<option value="[[item.value]]">[[item.label]]</option>
</template>
</select>
</span>
</section>
<section>
<span class="title">Require Signed-off-by in commit message</span>
<span class="value">
<select
id="useSignedOffBySelect"
is="gr-select"
bind-value="{{_projectConfig.use_signed_off_by.configured_value}}"
disabled$="[[_readOnly]]">>
<template is="dom-repeat"
items="[[_formatBooleanSelect(_projectConfig.use_signed_off_by)]]">
<option value="[[item.value]]">[[item.label]]</option>
</template>
</select>
</span>
</section>
</fieldset>
<!-- TODO @beckysiegel add plugin config widgets -->
<gr-button
on-tap="_handleSaveProjectConfig"
disabled$="[[_computeButtonDisabled(_readOnly, _configChanged)]]">Save changes</gr-button>
<h1 id="Title">[[project]]</h1>
<div id="loading" class$="[[_computeLoadingClass(_loading)]]">Loading...</div>
<div id="loadedContent" class$="[[_computeLoadingClass(_loading)]]">
<div id="downloadContent" class$="[[_computeDownloadClass(_schemes)]]">
<h2 id="download">Download</h2>
<fieldset>
<gr-download-commands
id="downloadCommands"
commands="[[_computeCommands(project, _schemesObj, _selectedScheme)]]"
schemes="[[_schemes]]"
selected-scheme="{{_selectedScheme}}"></gr-download-commands>
</fieldset>
</div>
<h2 id="configurations"
class$="[[_computeHeaderClass(_configChanged)]]">Configurations</h2>
<div id="form">
<fieldset>
<h3 id="Description">Description</h3>
<fieldset>
<iron-autogrow-textarea
id="descriptionInput"
class="description"
autocomplete="on"
placeholder="<Insert project description here>"
bind-value="{{_projectConfig.description}}"
disabled$="[[_readOnly]]"></iron-autogrow-textarea>
</fieldset>
<h3 id="Options">Project Options</h3>
<fieldset id="options">
<section>
<span class="title">State</span>
<span class="value">
<select
id="stateSelect"
is="gr-select"
bind-value="{{_projectConfig.state}}"
disabled$="[[_readOnly]]">
<template is="dom-repeat" items=[[_states]]>
<option value="[[item.value]]">[[item.label]]</option>
</template>
</select>
</span>
</section>
<section>
<span class="title">Submit type</span>
<span class="value">
<select
id="submitTypeSelect"
is="gr-select"
bind-value="{{_projectConfig.submit_type}}"
disabled$="[[_readOnly]]">>
<template is="dom-repeat" items="[[_submitTypes]]">
<option value="[[item.value]]">[[item.label]]</option>
</template>
</select>
</span>
</section>
<section>
<span class="title">Allow content merges</span>
<span class="value">
<select
id="contentMergeSelect"
is="gr-select"
bind-value="{{_projectConfig.use_content_merge.configured_value}}"
disabled$="[[_readOnly]]">>
<template is="dom-repeat"
items="[[_formatBooleanSelect(_projectConfig.use_content_merge)]]">
<option value="[[item.value]]">[[item.label]]</option>
</template>
</select>
</span>
</section>
<section>
<span class="title">
Create a new change for every commit not in the target branch
</span>
<span class="value">
<select
id="newChangeSelect"
is="gr-select"
bind-value="{{_projectConfig.create_new_change_for_all_not_in_target.configured_value}}"
disabled$="[[_readOnly]]">>
<template is="dom-repeat"
items="[[_formatBooleanSelect(_projectConfig.create_new_change_for_all_not_in_target)]]">
<option value="[[item.value]]">[[item.label]]</option>
</template>
</select>
</span>
</section>
<section>
<span class="title">Require Change-Id in commit message</span>
<span class="value">
<select
id="requireChangeIdSelect"
is="gr-select"
bind-value="{{_projectConfig.require_change_id.configured_value}}"
disabled$="[[_readOnly]]">>
<template is="dom-repeat"
items="[[_formatBooleanSelect(_projectConfig.require_change_id)]]">
<option value="[[item.value]]">[[item.label]]</option>
</template>
</select>
</span>
</section>
<section>
<span class="title">
Reject implicit merges when changes are pushed for review</span>
<span class="value">
<select
id="rejectImplicitMergesSelect"
is="gr-select"
bind-value="{{_projectConfig.reject_implicit_merges.configured_value}}"
disabled$="[[_readOnly]]">>
<template is="dom-repeat"
items="[[_formatBooleanSelect(_projectConfig.reject_implicit_merges)]]">
<option value="[[item.value]]">[[item.label]]</option>
</template>
</select>
</span>
</section>
<section>
<span class="title">Maximum Git object size limit</span>
<span class="value">
<input
id="maxGitObjSizeInput"
bind-value="{{_projectConfig.max_object_size_limit.configured_value}}"
is="iron-input"
type="text"
disabled$="[[_readOnly]]">
</span>
</section>
</fieldset>
<h3 id="Options">Contributor Agreements</h3>
<fieldset id="agreements">
<section>
<span class="title">
Require a valid contributor agreement to upload</span>
<span class="value">
<select
id="contributorAgreementSelect"
is="gr-select"
bind-value="{{_projectConfig.use_contributor_agreements.configured_value}}"
disabled$="[[_readOnly]]">>
<template is="dom-repeat"
items="[[_formatBooleanSelect(_projectConfig.use_contributor_agreements)]]">
<option value="[[item.value]]">[[item.label]]</option>
</template>
</select>
</span>
</section>
<section>
<span class="title">Require Signed-off-by in commit message</span>
<span class="value">
<select
id="useSignedOffBySelect"
is="gr-select"
bind-value="{{_projectConfig.use_signed_off_by.configured_value}}"
disabled$="[[_readOnly]]">>
<template is="dom-repeat"
items="[[_formatBooleanSelect(_projectConfig.use_signed_off_by)]]">
<option value="[[item.value]]">[[item.label]]</option>
</template>
</select>
</span>
</section>
</fieldset>
<!-- TODO @beckysiegel add plugin config widgets -->
<gr-button
on-tap="_handleSaveProjectConfig"
disabled$="[[_computeButtonDisabled(_readOnly, _configChanged)]]">Save changes</gr-button>
</fieldset>
</div>
</div>
</main>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>

View File

@@ -58,6 +58,11 @@
type: Boolean,
value: true,
},
_loggedIn: {
type: Boolean,
value: false,
observer: '_loggedInChanged',
},
_projectConfig: Object,
_readOnly: {
type: Boolean,
@@ -75,13 +80,24 @@
return Object.values(SUBMIT_TYPES);
},
},
_schemes: {
type: Array,
value() { return []; },
computed: '_computeSchemes(_schemesObj)',
observer: '_schemesChanged',
},
_selectedCommand: {
type: String,
value: 'Clone',
},
_selectedScheme: String,
_schemesObj: Object,
},
observers: [
'_handleConfigChanged(_projectConfig.*)',
],
attached() {
this._loadProject();
},
@@ -109,9 +125,31 @@
this._loading = false;
}));
promises.push(this.$.restAPI.getConfig().then(config => {
this._schemesObj = config.download.schemes;
}));
return Promise.all(promises);
},
_computeLoadingClass(loading) {
return loading ? 'loading' : '';
},
_computeDownloadClass(schemes) {
return !schemes || !schemes.length ? 'hideDownload' : '';
},
_loggedInChanged(_loggedIn) {
if (!_loggedIn) { return; }
this.$.restAPI.getPreferences().then(prefs => {
if (prefs.download_scheme) {
// Note (issue 5180): normalize the download scheme with lower-case.
this._selectedScheme = prefs.download_scheme.toLowerCase();
}
});
},
_formatBooleanSelect(item) {
if (!item) { return; }
let inheritLabel = 'Inherit';
@@ -174,5 +212,34 @@
_computeHeaderClass(configChanged) {
return configChanged ? 'edited' : '';
},
_computeSchemes(schemesObj) {
return Object.keys(schemesObj);
},
_schemesChanged(schemes) {
if (schemes.length === 0) { return; }
if (!schemes.includes(this._selectedScheme)) {
this._selectedScheme = schemes.sort()[0];
}
},
_computeCommands(project, schemesObj, _selectedScheme) {
const commands = [];
let commandObj;
if (schemesObj.hasOwnProperty(_selectedScheme)) {
commandObj = schemesObj[_selectedScheme].clone_commands;
}
for (const title in commandObj) {
if (!commandObj.hasOwnProperty(title)) { continue; }
commands.push({
title,
command: commandObj[title]
.replace('${project}', project)
.replace('${project-base-name}', project),
});
}
return commands;
},
});
})();

View File

@@ -36,6 +36,7 @@ limitations under the License.
let element;
let sandbox;
const PROJECT = 'test-project';
const SCHEMES = {http: {}, repo: {}, ssh: {}};
function getFormFields() {
const selects = Polymer.dom(element.root).querySelectorAll('select');
@@ -80,6 +81,9 @@ limitations under the License.
submit_type: 'MERGE_IF_NECESSARY',
});
},
getConfig() {
return Promise.resolve({download: {}});
},
});
element = fixture('basic');
});
@@ -89,8 +93,26 @@ limitations under the License.
});
test('loading displays before project config is loaded', () => {
assert.isFalse(element.$.loading.hidden);
assert.isTrue(element.$.form.hidden);
assert.isTrue(element.$.loading.classList.contains('loading'));
assert.isFalse(getComputedStyle(element.$.loading).display === 'none');
assert.isTrue(element.$.loadedContent.classList.contains('loading'));
assert.isTrue(getComputedStyle(element.$.loadedContent)
.display === 'none');
});
test('download commands visibility', () => {
element._loading = false;
flushAsynchronousOperations();
assert.isTrue(element.$.downloadContent.classList
.contains('hideDownload'));
assert.isTrue(getComputedStyle(element.$.downloadContent)
.display == 'none');
element._schemesObj = SCHEMES;
flushAsynchronousOperations();
assert.isFalse(element.$.downloadContent.classList
.contains('hideDownload'));
assert.isFalse(getComputedStyle(element.$.downloadContent)
.display == 'none');
});
test('form defaults to read only', () => {
@@ -237,7 +259,7 @@ limitations under the License.
configInputObj.use_signed_off_by;
assert.isFalse(button.hasAttribute('disabled'));
assert.isTrue(element.$.Title.classList.contains('edited'));
assert.isTrue(element.$.configurations.classList.contains('edited'));
const formattedObj =
element._formatProjectConfigForSave(element._projectConfig);