Make edit file input an autocomplete
Uses the /files endpoint to query possible files.
Bug: Issue 4437
Change-Id: I439100b5f85de05cba8988daa3fd71502b6af07f
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.html b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.html
index 7ddf9a9..62ed476 100644
--- a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.html
+++ b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.html
@@ -16,13 +16,14 @@
<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 @@
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 @@
<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>
\ No newline at end of file
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.js b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.js
index 62e8c7a..625f145 100644
--- a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.js
+++ b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.js
@@ -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};
+ }));
+ },
});
})();
\ No newline at end of file
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_test.html b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_test.html
index 6da4e32..d563393 100644
--- a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_test.html
+++ b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_test.html
@@ -37,12 +37,16 @@
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 @@
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 @@
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 @@
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');
});
});
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.js b/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.js
index c0449be..431ac0e 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.js
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.js
@@ -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() {
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.html b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.html
index 7aa7abf..ab847c4 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.html
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.html
@@ -38,31 +38,29 @@
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>
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
index c206b20..033d01b 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
@@ -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));
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
index eb4f418..aa7f9a0 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
@@ -701,6 +701,15 @@
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);