Add repository/branch picker element
Add a new element that simplifies user selection of a repository and branch pair named gr-repo-branch-picker. Also introduces a wrapper around gr-autocomplete that provides additional visual styling named gr-labeled-autocomplete. Change-Id: I434690b249fd4632989bbdd73cad6f5229399ced
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
<!--
|
||||
@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="../../shared/gr-autocomplete/gr-autocomplete.html">
|
||||
<link rel="import" href="../../../styles/shared-styles.html">
|
||||
|
||||
<dom-module id="gr-labeled-autocomplete">
|
||||
<template>
|
||||
<style include="shared-styles">
|
||||
:host {
|
||||
display: block;
|
||||
width: 12em;
|
||||
}
|
||||
#container {
|
||||
background: var(--chip-background-color);
|
||||
border-radius: 1em;
|
||||
padding: .5em;
|
||||
}
|
||||
#header {
|
||||
color: var(--deemphasized-text-color);
|
||||
font-family: var(--font-family-bold);
|
||||
font-size: var(--font-size-small);
|
||||
}
|
||||
#body {
|
||||
display: flex;
|
||||
}
|
||||
gr-autocomplete {
|
||||
height: 1.5em;
|
||||
--gr-autocomplete: {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
#trigger {
|
||||
border-left: 1px solid var(--deemphasized-text-color);
|
||||
color: var(--deemphasized-text-color);
|
||||
cursor: pointer;
|
||||
padding-left: .4em;
|
||||
}
|
||||
#trigger:hover {
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
</style>
|
||||
<div id="container">
|
||||
<div id="header">[[label]]</div>
|
||||
<div id="body">
|
||||
<gr-autocomplete
|
||||
id="autocomplete"
|
||||
threshold="[[_autocompleteThreshold]]"
|
||||
query="[[query]]"
|
||||
disabled="[[disabled]]"
|
||||
placeholder="[[placeholder]]"
|
||||
borderless></gr-autocomplete>
|
||||
<div id="trigger" on-tap="_handleTriggerTap">▼</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script src="gr-labeled-autocomplete.js"></script>
|
||||
</dom-module>
|
||||
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* @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-labeled-autocomplete',
|
||||
|
||||
/**
|
||||
* Fired when a value is chosen.
|
||||
*
|
||||
* @event commit
|
||||
*/
|
||||
|
||||
properties: {
|
||||
|
||||
/**
|
||||
* Used just like the query property of gr-autocomplete.
|
||||
*
|
||||
* @type {function(string): Promise<?>}
|
||||
*/
|
||||
query: {
|
||||
type: Function,
|
||||
value() {
|
||||
return function() {
|
||||
return Promise.resolve([]);
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
text: {
|
||||
type: String,
|
||||
value: '',
|
||||
notify: true,
|
||||
},
|
||||
label: String,
|
||||
placeholder: String,
|
||||
disabled: Boolean,
|
||||
|
||||
_autocompleteThreshold: {
|
||||
type: Number,
|
||||
value: 0,
|
||||
readOnly: true,
|
||||
},
|
||||
},
|
||||
|
||||
_handleTriggerTap() {
|
||||
this.$.autocomplete.focus();
|
||||
},
|
||||
|
||||
setText(text) {
|
||||
this.$.autocomplete.setText(text);
|
||||
},
|
||||
|
||||
clear() {
|
||||
this.setText('');
|
||||
},
|
||||
});
|
||||
})();
|
||||
@@ -0,0 +1,58 @@
|
||||
<!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-labeled-autocomplete</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-labeled-autocomplete.html">
|
||||
|
||||
<script>void(0);</script>
|
||||
|
||||
<test-fixture id="basic">
|
||||
<template>
|
||||
<gr-labeled-autocomplete></gr-labeled-autocomplete>
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
<script>
|
||||
suite('gr-labeled-autocomplete tests', () => {
|
||||
let element;
|
||||
let sandbox;
|
||||
|
||||
setup(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
element = fixture('basic');
|
||||
});
|
||||
|
||||
teardown(() => { sandbox.restore(); });
|
||||
|
||||
test('tapping trigger focuses autocomplete', () => {
|
||||
sandbox.stub(element.$.autocomplete, 'focus');
|
||||
element._handleTriggerTap();
|
||||
assert.isTrue(element.$.autocomplete.focus.calledOnce);
|
||||
});
|
||||
|
||||
test('setText', () => {
|
||||
sandbox.stub(element.$.autocomplete, 'setText');
|
||||
element.setText('foo-bar');
|
||||
assert.isTrue(element.$.autocomplete.setText.calledWith('foo-bar'));
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,60 @@
|
||||
<!--
|
||||
@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-icon/iron-icon.html">
|
||||
<link rel="import" href="../../../styles/shared-styles.html">
|
||||
<link rel="import" href="../../../behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior.html">
|
||||
<link rel="import" href="../../shared/gr-icons/gr-icons.html">
|
||||
<link rel="import" href="../../shared/gr-labeled-autocomplete/gr-labeled-autocomplete.html">
|
||||
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
|
||||
|
||||
<dom-module id="gr-repo-branch-picker">
|
||||
<template>
|
||||
<style include="shared-styles">
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
gr-labeled-autocomplete,
|
||||
iron-icon {
|
||||
display: inline-block;
|
||||
}
|
||||
iron-icon {
|
||||
margin-bottom: 1.2em;
|
||||
}
|
||||
</style>
|
||||
<div>
|
||||
<gr-labeled-autocomplete
|
||||
id="repoInput"
|
||||
label="Repository"
|
||||
placeholder="Select repo"
|
||||
on-commit="_repoCommitted"
|
||||
query="[[_repoQuery]]">
|
||||
</gr-labeled-autocomplete>
|
||||
<iron-icon icon="gr-icons:chevron-right"></iron-icon>
|
||||
<gr-labeled-autocomplete
|
||||
id="branchInput"
|
||||
label="Branch"
|
||||
placeholder="Select branch"
|
||||
disabled="[[_branchDisabled]]"
|
||||
on-commit="_branchCommitted"
|
||||
query="[[_query]]">
|
||||
</gr-labeled-autocomplete>
|
||||
</div>
|
||||
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
|
||||
</template>
|
||||
<script src="gr-repo-branch-picker.js"></script>
|
||||
</dom-module>
|
||||
@@ -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.
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
const SUGGESTIONS_LIMIT = 15;
|
||||
const REF_PREFIX = 'refs/heads/';
|
||||
|
||||
Polymer({
|
||||
is: 'gr-repo-branch-picker',
|
||||
|
||||
properties: {
|
||||
repo: {
|
||||
type: String,
|
||||
notify: true,
|
||||
observer: '_repoChanged',
|
||||
},
|
||||
branch: {
|
||||
type: String,
|
||||
notify: true,
|
||||
},
|
||||
_branchDisabled: Boolean,
|
||||
_query: {
|
||||
type: Function,
|
||||
value() {
|
||||
return this._getRepoBranchesSuggestions.bind(this);
|
||||
},
|
||||
},
|
||||
_repoQuery: {
|
||||
type: Function,
|
||||
value() {
|
||||
return this._getRepoSuggestions.bind(this);
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
behaviors: [
|
||||
Gerrit.URLEncodingBehavior,
|
||||
],
|
||||
|
||||
attached() {
|
||||
if (this.repo) {
|
||||
this.$.repoInput.setText(this.repo);
|
||||
}
|
||||
},
|
||||
|
||||
ready() {
|
||||
this._branchDisabled = !this.repo;
|
||||
},
|
||||
|
||||
_getRepoBranchesSuggestions(input) {
|
||||
if (!this.repo) { return Promise.resolve([]); }
|
||||
if (input.startsWith(REF_PREFIX)) {
|
||||
input = input.substring(REF_PREFIX.length);
|
||||
}
|
||||
return this.$.restAPI.getRepoBranches(input, this.repo, SUGGESTIONS_LIMIT)
|
||||
.then(this._branchResponseToSuggestions.bind(this));
|
||||
},
|
||||
|
||||
_getRepoSuggestions(input) {
|
||||
return this.$.restAPI.getRepos(input, SUGGESTIONS_LIMIT)
|
||||
.then(this._repoResponseToSuggestions.bind(this));
|
||||
},
|
||||
|
||||
_repoResponseToSuggestions(res) {
|
||||
return res.map(repo => ({
|
||||
name: repo.name,
|
||||
value: this.singleDecodeURL(repo.id),
|
||||
}));
|
||||
},
|
||||
|
||||
_branchResponseToSuggestions(res) {
|
||||
return Object.keys(res).map(key => {
|
||||
let branch = res[key].ref;
|
||||
if (branch.startsWith(REF_PREFIX)) {
|
||||
branch = branch.substring(REF_PREFIX.length);
|
||||
}
|
||||
return {name: branch, value: branch};
|
||||
});
|
||||
},
|
||||
|
||||
_repoCommitted(e) {
|
||||
this.repo = e.detail.value;
|
||||
},
|
||||
|
||||
_branchCommitted(e) {
|
||||
this.branch = e.detail.value;
|
||||
},
|
||||
|
||||
_repoChanged() {
|
||||
this.$.branchInput.clear();
|
||||
this._branchDisabled = !this.repo;
|
||||
},
|
||||
});
|
||||
})();
|
||||
@@ -0,0 +1,140 @@
|
||||
<!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-branch-picker</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-branch-picker.html">
|
||||
|
||||
<script>void(0);</script>
|
||||
|
||||
<test-fixture id="basic">
|
||||
<template>
|
||||
<gr-repo-branch-picker></gr-repo-branch-picker>
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
<script>
|
||||
suite('gr-repo-branch-picker tests', () => {
|
||||
let element;
|
||||
let sandbox;
|
||||
|
||||
setup(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
element = fixture('basic');
|
||||
});
|
||||
|
||||
teardown(() => { sandbox.restore(); });
|
||||
|
||||
suite('_getRepoSuggestions', () => {
|
||||
setup(() => {
|
||||
sandbox.stub(element.$.restAPI, 'getRepos')
|
||||
.returns(Promise.resolve([
|
||||
{
|
||||
id: 'plugins%2Favatars-external',
|
||||
name: 'plugins/avatars-external',
|
||||
}, {
|
||||
id: 'plugins%2Favatars-gravatar',
|
||||
name: 'plugins/avatars-gravatar',
|
||||
}, {
|
||||
id: 'plugins%2Favatars%2Fexternal',
|
||||
name: 'plugins/avatars/external',
|
||||
}, {
|
||||
id: 'plugins%2Favatars%2Fgravatar',
|
||||
name: 'plugins/avatars/gravatar',
|
||||
},
|
||||
]));
|
||||
});
|
||||
|
||||
test('converts to suggestion objects', () => {
|
||||
const input = 'plugins/avatars';
|
||||
return element._getRepoSuggestions(input).then(suggestions => {
|
||||
assert.isTrue(element.$.restAPI.getRepos.calledWith(input));
|
||||
const unencodedNames = [
|
||||
'plugins/avatars-external',
|
||||
'plugins/avatars-gravatar',
|
||||
'plugins/avatars/external',
|
||||
'plugins/avatars/gravatar',
|
||||
];
|
||||
assert.deepEqual(suggestions.map(s => s.name), unencodedNames);
|
||||
assert.deepEqual(suggestions.map(s => s.value), unencodedNames);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('_getRepoBranchesSuggestions', () => {
|
||||
setup(() => {
|
||||
sandbox.stub(element.$.restAPI, 'getRepoBranches')
|
||||
.returns(Promise.resolve([
|
||||
{ref: 'refs/heads/stable-2.10'},
|
||||
{ref: 'refs/heads/stable-2.11'},
|
||||
{ref: 'refs/heads/stable-2.12'},
|
||||
{ref: 'refs/heads/stable-2.13'},
|
||||
{ref: 'refs/heads/stable-2.14'},
|
||||
{ref: 'refs/heads/stable-2.15'},
|
||||
]));
|
||||
});
|
||||
|
||||
test('converts to suggestion objects', () => {
|
||||
const repo = 'gerrit';
|
||||
const branchInput = 'stable-2.1';
|
||||
element.repo = repo;
|
||||
return element._getRepoBranchesSuggestions(branchInput)
|
||||
.then(suggestions => {
|
||||
assert.isTrue(element.$.restAPI.getRepoBranches.calledWith(
|
||||
branchInput, repo, 15));
|
||||
const refNames = [
|
||||
'stable-2.10',
|
||||
'stable-2.11',
|
||||
'stable-2.12',
|
||||
'stable-2.13',
|
||||
'stable-2.14',
|
||||
'stable-2.15',
|
||||
];
|
||||
assert.deepEqual(suggestions.map(s => s.name), refNames);
|
||||
assert.deepEqual(suggestions.map(s => s.value), refNames);
|
||||
});
|
||||
});
|
||||
|
||||
test('filters out ref prefix', () => {
|
||||
const repo = 'gerrit';
|
||||
const branchInput = 'refs/heads/stable-2.1';
|
||||
element.repo = repo;
|
||||
return element._getRepoBranchesSuggestions(branchInput)
|
||||
.then(suggestions => {
|
||||
assert.isTrue(element.$.restAPI.getRepoBranches.calledWith(
|
||||
'stable-2.1', repo, 15));
|
||||
});
|
||||
});
|
||||
|
||||
test('does not query when repo is unset', () => {
|
||||
return element._getRepoBranchesSuggestions('')
|
||||
.then(() => {
|
||||
assert.isFalse(element.$.restAPI.getRepoBranches.called);
|
||||
element.repo = 'gerrit';
|
||||
return element._getRepoBranchesSuggestions('');
|
||||
})
|
||||
.then(() => {
|
||||
assert.isTrue(element.$.restAPI.getRepoBranches.called);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -167,12 +167,14 @@ limitations under the License.
|
||||
'shared/gr-js-api-interface/gr-plugin-endpoints_test.html',
|
||||
'shared/gr-js-api-interface/gr-plugin-rest-api_test.html',
|
||||
'shared/gr-fixed-panel/gr-fixed-panel_test.html',
|
||||
'shared/gr-labeled-autocomplete/gr-labeled-autocomplete_test.html',
|
||||
'shared/gr-lib-loader/gr-lib-loader_test.html',
|
||||
'shared/gr-limited-text/gr-limited-text_test.html',
|
||||
'shared/gr-linked-chip/gr-linked-chip_test.html',
|
||||
'shared/gr-linked-text/gr-linked-text_test.html',
|
||||
'shared/gr-list-view/gr-list-view_test.html',
|
||||
'shared/gr-page-nav/gr-page-nav_test.html',
|
||||
'shared/gr-repo-branch-picker/gr-repo-branch-picker_test.html',
|
||||
'shared/gr-rest-api-interface/gr-auth_test.html',
|
||||
'shared/gr-rest-api-interface/gr-rest-api-interface_test.html',
|
||||
'shared/gr-rest-api-interface/gr-reviewer-updates-parser_test.html',
|
||||
|
||||
Reference in New Issue
Block a user