Make edit file input an autocomplete
Uses the /files endpoint to query possible files. Bug: Issue 4437 Change-Id: I439100b5f85de05cba8988daa3fd71502b6af07f
This commit is contained in:
@@ -16,13 +16,14 @@ limitations under the License.
|
||||
|
||||
<link rel="import" href="../../../bower_components/polymer/polymer.html">
|
||||
|
||||
<link rel="import" href="../../../bower_components/paper-input/paper-input.html">
|
||||
<link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
|
||||
<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
|
||||
<link rel="import" href="../../shared/gr-autocomplete/gr-autocomplete.html">
|
||||
<link rel="import" href="../../shared/gr-button/gr-button.html">
|
||||
<link rel="import" href="../../shared/gr-confirm-dialog/gr-confirm-dialog.html">
|
||||
<link rel="import" href="../../shared/gr-dropdown/gr-dropdown.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="../../../styles/shared-styles.html">
|
||||
|
||||
@@ -41,21 +42,21 @@ limitations under the License.
|
||||
margin-left: 1em;
|
||||
text-decoration: none;
|
||||
}
|
||||
paper-input {
|
||||
--paper-input-container: {
|
||||
padding: 0;
|
||||
min-width: 15em;
|
||||
}
|
||||
--paper-input-container-input: {
|
||||
font-size: 1em;
|
||||
}
|
||||
}
|
||||
gr-confirm-dialog {
|
||||
width: 50em;
|
||||
}
|
||||
gr-confirm-dialog .main {
|
||||
width: 100%;
|
||||
}
|
||||
gr-autocomplete {
|
||||
--gr-autocomplete: {
|
||||
border: 1px solid #d1d2d3;
|
||||
border-radius: 2px;
|
||||
font-size: 1em;
|
||||
height: 2em;
|
||||
padding: 0 .15em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<template is="dom-repeat" items="[[_actions]]" as="action">
|
||||
<gr-button
|
||||
@@ -74,13 +75,15 @@ limitations under the License.
|
||||
<div class="header">Edit a file</div>
|
||||
<div class="main">
|
||||
<!-- TODO(kaspern): Make this an autocomplete. -->
|
||||
<paper-input
|
||||
<gr-autocomplete
|
||||
class="input"
|
||||
label="Enter an existing or new full file path."
|
||||
value="{{_path}}"></paper-input>
|
||||
placeholder="Enter an existing or new full file path."
|
||||
query="[[_query]]"
|
||||
text="{{_path}}"></gr-autocomplete>
|
||||
</div>
|
||||
</gr-confirm-dialog>
|
||||
</gr-overlay>
|
||||
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
|
||||
</template>
|
||||
<script src="gr-edit-controls.js"></script>
|
||||
</dom-module>
|
@@ -37,8 +37,18 @@
|
||||
type: String,
|
||||
value: '',
|
||||
},
|
||||
_query: {
|
||||
type: Function,
|
||||
value() {
|
||||
return this._queryFiles.bind(this);
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
behaviors: [
|
||||
Gerrit.PatchSetBehavior,
|
||||
],
|
||||
|
||||
_handleTap(e) {
|
||||
e.preventDefault();
|
||||
const action = Polymer.dom(e).localTarget.id;
|
||||
@@ -73,7 +83,8 @@
|
||||
},
|
||||
|
||||
_closeDialog(dialog) {
|
||||
dialog.querySelectorAll('.input').forEach(input => { input.value = ''; });
|
||||
dialog.querySelectorAll('gr-autocomplete')
|
||||
.forEach(input => { input.text = ''; });
|
||||
dialog.classList.toggle('invisible', true);
|
||||
return this.$.overlay.close();
|
||||
},
|
||||
@@ -87,5 +98,12 @@
|
||||
Gerrit.Nav.navigateToRelativeUrl(url);
|
||||
this._closeDialog(Polymer.dom(e).localTarget);
|
||||
},
|
||||
|
||||
_queryFiles(input) {
|
||||
return this.$.restAPI.queryChangeFiles(this.change._number,
|
||||
this.EDIT_NAME, input).then(res => res.map(file => {
|
||||
return {name: file};
|
||||
}));
|
||||
},
|
||||
});
|
||||
})();
|
@@ -37,12 +37,16 @@ suite('gr-edit-controls tests', () => {
|
||||
let sandbox;
|
||||
let showDialogSpy;
|
||||
let closeDialogSpy;
|
||||
let queryStub;
|
||||
|
||||
setup(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
element = fixture('basic');
|
||||
element.change = {_number: '42'};
|
||||
showDialogSpy = sandbox.spy(element, '_showDialog');
|
||||
closeDialogSpy = sandbox.spy(element, '_closeDialog');
|
||||
queryStub = sandbox.stub(element.$.restAPI, 'queryChangeFiles')
|
||||
.returns(Promise.resolve([]));
|
||||
flushAsynchronousOperations();
|
||||
});
|
||||
|
||||
@@ -67,7 +71,9 @@ suite('gr-edit-controls tests', () => {
|
||||
MockInteractions.tap(element.$$('#edit'));
|
||||
return showDialogSpy.lastCall.returnValue.then(() => {
|
||||
assert.isTrue(element.$.editDialog.disabled);
|
||||
element._path = 'src/test.cpp';
|
||||
assert.isFalse(queryStub.called);
|
||||
element.$.editDialog.querySelector('.input').text = 'src/test.cpp';
|
||||
assert.isTrue(queryStub.called);
|
||||
assert.isFalse(element.$.editDialog.disabled);
|
||||
MockInteractions.tap(element.$.editDialog.$$('gr-button[primary]'));
|
||||
for (const stub of navStubs) { assert.isTrue(stub.called); }
|
||||
@@ -79,7 +85,7 @@ suite('gr-edit-controls tests', () => {
|
||||
MockInteractions.tap(element.$$('#edit'));
|
||||
return showDialogSpy.lastCall.returnValue.then(() => {
|
||||
assert.isTrue(element.$.editDialog.disabled);
|
||||
element._path = 'src/test.cpp';
|
||||
element.$.editDialog.querySelector('.input').text = 'src/test.cpp';
|
||||
assert.isFalse(element.$.editDialog.disabled);
|
||||
MockInteractions.tap(element.$.editDialog.$$('gr-button'));
|
||||
for (const stub of navStubs) { assert.isFalse(stub.called); }
|
||||
@@ -92,7 +98,7 @@ suite('gr-edit-controls tests', () => {
|
||||
test('openEditDialog', () => {
|
||||
return element.openEditDialog('test/path.cpp').then(() => {
|
||||
assert.isFalse(element.$.editDialog.hasAttribute('hidden'));
|
||||
assert.equal(element.$.editDialog.querySelector('.input').value,
|
||||
assert.equal(element.$.editDialog.querySelector('.input').text,
|
||||
'test/path.cpp');
|
||||
});
|
||||
});
|
||||
|
@@ -46,6 +46,7 @@
|
||||
},
|
||||
suggestions: {
|
||||
type: Array,
|
||||
value: () => [],
|
||||
observer: '_resetCursorStops',
|
||||
},
|
||||
_suggestionEls: {
|
||||
@@ -151,8 +152,12 @@
|
||||
},
|
||||
|
||||
_resetCursorStops() {
|
||||
Polymer.dom.flush();
|
||||
this._suggestionEls = this.$.suggestions.querySelectorAll('li');
|
||||
if (this.suggestions.length > 0) {
|
||||
Polymer.dom.flush();
|
||||
this._suggestionEls = this.$.suggestions.querySelectorAll('li');
|
||||
} else {
|
||||
this._suggestionEls = [];
|
||||
}
|
||||
},
|
||||
|
||||
_resetCursorIndex() {
|
||||
|
@@ -38,31 +38,29 @@ limitations under the License.
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
<div>
|
||||
<input
|
||||
id="input"
|
||||
class$="[[_computeClass(borderless)]]"
|
||||
is="iron-input"
|
||||
disabled$="[[disabled]]"
|
||||
bind-value="{{text}}"
|
||||
placeholder="[[placeholder]]"
|
||||
on-keydown="_handleKeydown"
|
||||
on-focus="_onInputFocus"
|
||||
on-blur="_onInputBlur"
|
||||
autocomplete="off"/>
|
||||
<gr-autocomplete-dropdown
|
||||
vertical-align="top"
|
||||
vertical-offset="20"
|
||||
horizontal-align="auto"
|
||||
id="suggestions"
|
||||
on-item-selected="_handleItemSelect"
|
||||
on-keydown="_handleKeydown"
|
||||
suggestions="[[_suggestions]]"
|
||||
role="listbox"
|
||||
index="[[_index]]"
|
||||
position-target="[[_inputElement]]">
|
||||
</gr-autocomplete-dropdown>
|
||||
</div>
|
||||
<input
|
||||
id="input"
|
||||
class$="[[_computeClass(borderless)]]"
|
||||
is="iron-input"
|
||||
disabled$="[[disabled]]"
|
||||
bind-value="{{text}}"
|
||||
placeholder="[[placeholder]]"
|
||||
on-keydown="_handleKeydown"
|
||||
on-focus="_onInputFocus"
|
||||
on-blur="_onInputBlur"
|
||||
autocomplete="off"/>
|
||||
<gr-autocomplete-dropdown
|
||||
vertical-align="top"
|
||||
vertical-offset="20"
|
||||
horizontal-align="auto"
|
||||
id="suggestions"
|
||||
on-item-selected="_handleItemSelect"
|
||||
on-keydown="_handleKeydown"
|
||||
suggestions="[[_suggestions]]"
|
||||
role="listbox"
|
||||
index="[[_index]]"
|
||||
position-target="[[_inputElement]]">
|
||||
</gr-autocomplete-dropdown>
|
||||
</template>
|
||||
<script src="gr-autocomplete.js"></script>
|
||||
</dom-module>
|
||||
|
@@ -904,6 +904,17 @@
|
||||
patchRange.patchNum);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {number|string} changeNum
|
||||
* @param {number|string} patchNum
|
||||
* @param {string} query
|
||||
* @return {!Promise<!Object>}
|
||||
*/
|
||||
queryChangeFiles(changeNum, patchNum, query) {
|
||||
return this._getChangeURLAndFetch(changeNum,
|
||||
`/files?q=${encodeURIComponent(query)}`, patchNum);
|
||||
},
|
||||
|
||||
getChangeFilesAsSpeciallySortedArray(changeNum, patchRange) {
|
||||
return this.getChangeFiles(changeNum, patchRange).then(
|
||||
this._normalizeChangeFilesResponse.bind(this));
|
||||
|
@@ -701,6 +701,15 @@ limitations under the License.
|
||||
assert.equal(sendStub.lastCall.args[1], '/projects/x%2Fy');
|
||||
});
|
||||
|
||||
test('queryChangeFiles', () => {
|
||||
const fetchStub = sandbox.stub(element, '_getChangeURLAndFetch')
|
||||
.returns(Promise.resolve());
|
||||
return element.queryChangeFiles('42', 'edit', 'test/path.js').then(() => {
|
||||
assert.deepEqual(fetchStub.lastCall.args,
|
||||
['42', '/files?q=test%2Fpath.js', 'edit']);
|
||||
});
|
||||
});
|
||||
|
||||
test('getProjects', () => {
|
||||
sandbox.stub(element, '_fetchSharedCacheURL');
|
||||
element.getProjects('test', 25);
|
||||
|
Reference in New Issue
Block a user