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