Add name label to account search
This change involves a restructuring of the way the autocomplete queries return. Instead of an array of strings, the query returns an array of objects with text and optional label properties. Bug: Issue 8448 Change-Id: Ie6e973473feb3d61b9e4e9dcac2ee981eed39745
This commit is contained in:
@@ -194,7 +194,7 @@
|
||||
* to _getSearchSuggestions.
|
||||
* @param {string} input - The full search term, in lowercase.
|
||||
* @return {!Promise} This returns a promise that resolves to an array of
|
||||
* strings.
|
||||
* suggestion objects.
|
||||
*/
|
||||
_fetchSuggestions(input) {
|
||||
// Split the input on colon to get a two part predicate/expression.
|
||||
@@ -226,7 +226,8 @@
|
||||
|
||||
default:
|
||||
return Promise.resolve(SEARCH_OPERATORS_WITH_NEGATIONS
|
||||
.filter(operator => operator.includes(input)));
|
||||
.filter(operator => operator.includes(input))
|
||||
.map(operator => ({text: operator})));
|
||||
}
|
||||
},
|
||||
|
||||
@@ -234,7 +235,7 @@
|
||||
* Get the sorted, pruned list of suggestions for the current search query.
|
||||
* @param {string} input - The complete search query.
|
||||
* @return {!Promise} This returns a promise that resolves to an array of
|
||||
* strings.
|
||||
* suggestions.
|
||||
*/
|
||||
_getSearchSuggestions(input) {
|
||||
// Allow spaces within quoted terms.
|
||||
@@ -242,15 +243,15 @@
|
||||
const trimmedInput = tokens[tokens.length - 1].toLowerCase();
|
||||
|
||||
return this._fetchSuggestions(trimmedInput)
|
||||
.then(operators => {
|
||||
if (!operators || !operators.length) { return []; }
|
||||
return operators
|
||||
.then(suggestions => {
|
||||
if (!suggestions || !suggestions.length) { return []; }
|
||||
return suggestions
|
||||
// Prioritize results that start with the input.
|
||||
.sort((a, b) => {
|
||||
const aContains = a.toLowerCase().indexOf(trimmedInput);
|
||||
const bContains = b.toLowerCase().indexOf(trimmedInput);
|
||||
const aContains = a.text.toLowerCase().indexOf(trimmedInput);
|
||||
const bContains = b.text.toLowerCase().indexOf(trimmedInput);
|
||||
if (aContains === bContains) {
|
||||
return a.localeCompare(b);
|
||||
return a.text.localeCompare(b.text);
|
||||
}
|
||||
if (aContains === -1) {
|
||||
return 1;
|
||||
@@ -263,10 +264,11 @@
|
||||
// Return only the first {MAX_AUTOCOMPLETE_RESULTS} results.
|
||||
.slice(0, MAX_AUTOCOMPLETE_RESULTS - 1)
|
||||
// Map to an object to play nice with gr-autocomplete.
|
||||
.map(operator => {
|
||||
.map(({text, label}) => {
|
||||
return {
|
||||
name: operator,
|
||||
value: operator,
|
||||
name: text,
|
||||
value: text,
|
||||
label,
|
||||
};
|
||||
});
|
||||
});
|
||||
|
@@ -99,7 +99,7 @@ limitations under the License.
|
||||
suite('_getSearchSuggestions', () => {
|
||||
test('Autocompletes accounts', () => {
|
||||
sandbox.stub(element, 'accountSuggestions', () =>
|
||||
Promise.resolve(['owner:fred@goog.co'])
|
||||
Promise.resolve([{text: 'owner:fred@goog.co'}])
|
||||
);
|
||||
return element._getSearchSuggestions('owner:fr').then(s => {
|
||||
assert.equal(s[0].value, 'owner:fred@goog.co');
|
||||
@@ -109,8 +109,8 @@ limitations under the License.
|
||||
test('Autocompletes groups', done => {
|
||||
sandbox.stub(element, 'groupSuggestions', () =>
|
||||
Promise.resolve([
|
||||
'ownerin:Polygerrit',
|
||||
'ownerin:gerrit',
|
||||
{text: 'ownerin:Polygerrit'},
|
||||
{text: 'ownerin:gerrit'},
|
||||
])
|
||||
);
|
||||
element._getSearchSuggestions('ownerin:pol').then(s => {
|
||||
@@ -122,9 +122,9 @@ limitations under the License.
|
||||
test('Autocompletes projects', done => {
|
||||
sandbox.stub(element, 'projectSuggestions', () =>
|
||||
Promise.resolve([
|
||||
'project:Polygerrit',
|
||||
'project:gerrit',
|
||||
'project:gerrittest',
|
||||
{text: 'project:Polygerrit'},
|
||||
{text: 'project:gerrit'},
|
||||
{text: 'project:gerrittest'},
|
||||
])
|
||||
);
|
||||
element._getSearchSuggestions('project:pol').then(s => {
|
||||
|
@@ -84,7 +84,7 @@
|
||||
.then(projects => {
|
||||
if (!projects) { return []; }
|
||||
const keys = Object.keys(projects);
|
||||
return keys.map(key => predicate + ':' + key);
|
||||
return keys.map(key => ({text: predicate + ':' + key}));
|
||||
});
|
||||
},
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
.then(groups => {
|
||||
if (!groups) { return []; }
|
||||
const keys = Object.keys(groups);
|
||||
return keys.map(key => predicate + ':' + key);
|
||||
return keys.map(key => ({text: predicate + ':' + key}));
|
||||
});
|
||||
},
|
||||
|
||||
@@ -125,20 +125,28 @@
|
||||
MAX_AUTOCOMPLETE_RESULTS)
|
||||
.then(accounts => {
|
||||
if (!accounts) { return []; }
|
||||
return accounts.map(acct => acct.email ?
|
||||
`${predicate}:${acct.email}` :
|
||||
`${predicate}:"${this._accountOrAnon(acct)}"`);
|
||||
return this._mapAccountsHelper(accounts, predicate);
|
||||
}).then(accounts => {
|
||||
// When the expression supplied is a beginning substring of 'self',
|
||||
// add it as an autocomplete option.
|
||||
if (SELF_EXPRESSION.startsWith(expression)) {
|
||||
return accounts.concat([predicate + ':' + SELF_EXPRESSION]);
|
||||
return accounts.concat(
|
||||
[{text: predicate + ':' + SELF_EXPRESSION}]);
|
||||
} else if (ME_EXPRESSION.startsWith(expression)) {
|
||||
return accounts.concat([predicate + ':' + ME_EXPRESSION]);
|
||||
return accounts.concat([{text: predicate + ':' + ME_EXPRESSION}]);
|
||||
} else {
|
||||
return accounts;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_mapAccountsHelper(accounts, predicate) {
|
||||
return accounts.map(account => ({
|
||||
label: account.name || '',
|
||||
text: account.email ?
|
||||
`${predicate}:${account.email}` :
|
||||
`${predicate}:"${this._accountOrAnon(account)}"`,
|
||||
}));
|
||||
},
|
||||
});
|
||||
})();
|
||||
|
@@ -57,11 +57,11 @@ limitations under the License.
|
||||
])
|
||||
);
|
||||
return element._fetchAccounts('owner', 'fr').then(s => {
|
||||
assert.equal(s[0], 'owner:fred@goog.co');
|
||||
assert.deepEqual(s[0], {text: 'owner:fred@goog.co', label: 'fred'});
|
||||
});
|
||||
});
|
||||
|
||||
test('Inserts self as option when valid', done => {
|
||||
test('Inserts self as option when valid', () => {
|
||||
sandbox.stub(element.$.restAPI, 'getSuggestedAccounts', () =>
|
||||
Promise.resolve([
|
||||
{
|
||||
@@ -71,17 +71,14 @@ limitations under the License.
|
||||
])
|
||||
);
|
||||
element._fetchAccounts('owner', 's').then(s => {
|
||||
assert.equal(s[0], 'owner:fred@goog.co');
|
||||
assert.equal(s[1], 'owner:self');
|
||||
}).then(() => {
|
||||
element._fetchAccounts('owner', 'selfs').then(s => {
|
||||
assert.notEqual(s[0], 'owner:self');
|
||||
done();
|
||||
});
|
||||
assert.deepEqual(s[0], {text: 'owner:fred@goog.co', label: 'fred'});
|
||||
assert.deepEqual(s[1], {text: 'owner:self'});
|
||||
}).then(() => element._fetchAccounts('owner', 'selfs')).then(s => {
|
||||
assert.notEqual(s[0], {text: 'owner:self'});
|
||||
});
|
||||
});
|
||||
|
||||
test('Inserts me as option when valid', done => {
|
||||
test('Inserts me as option when valid', () => {
|
||||
sandbox.stub(element.$.restAPI, 'getSuggestedAccounts', () =>
|
||||
Promise.resolve([
|
||||
{
|
||||
@@ -90,18 +87,15 @@ limitations under the License.
|
||||
},
|
||||
])
|
||||
);
|
||||
element._fetchAccounts('owner', 'm').then(s => {
|
||||
assert.equal(s[0], 'owner:fred@goog.co');
|
||||
assert.equal(s[1], 'owner:me');
|
||||
}).then(() => {
|
||||
element._fetchAccounts('owner', 'meme').then(s => {
|
||||
assert.notEqual(s[0], 'owner:me');
|
||||
done();
|
||||
});
|
||||
return element._fetchAccounts('owner', 'm').then(s => {
|
||||
assert.deepEqual(s[0], {text: 'owner:fred@goog.co', label: 'fred'});
|
||||
assert.deepEqual(s[1], {text: 'owner:me'});
|
||||
}).then(() => element._fetchAccounts('owner', 'meme')).then(s => {
|
||||
assert.notEqual(s[0], {text: 'owner:me'});
|
||||
});
|
||||
});
|
||||
|
||||
test('Autocompletes groups', done => {
|
||||
test('Autocompletes groups', () => {
|
||||
sandbox.stub(element.$.restAPI, 'getSuggestedGroups', () =>
|
||||
Promise.resolve({
|
||||
Polygerrit: 0,
|
||||
@@ -109,25 +103,20 @@ limitations under the License.
|
||||
gerrittest: 0,
|
||||
})
|
||||
);
|
||||
element._fetchGroups('ownerin', 'pol').then(s => {
|
||||
assert.equal(s[0], 'ownerin:Polygerrit');
|
||||
done();
|
||||
return element._fetchGroups('ownerin', 'pol').then(s => {
|
||||
assert.deepEqual(s[0], {text: 'ownerin:Polygerrit'});
|
||||
});
|
||||
});
|
||||
|
||||
test('Autocompletes projects', done => {
|
||||
test('Autocompletes projects', () => {
|
||||
sandbox.stub(element.$.restAPI, 'getSuggestedProjects', () =>
|
||||
Promise.resolve({
|
||||
Polygerrit: 0,
|
||||
})
|
||||
);
|
||||
element._fetchProjects('project', 'pol').then(s => {
|
||||
assert.equal(s[0], 'project:Polygerrit');
|
||||
done();
|
||||
Promise.resolve({Polygerrit: 0}));
|
||||
return element._fetchProjects('project', 'pol').then(s => {
|
||||
assert.deepEqual(s[0], {text: 'project:Polygerrit'});
|
||||
});
|
||||
});
|
||||
|
||||
test('Autocomplete doesnt override exact matches to input', done => {
|
||||
test('Autocomplete doesnt override exact matches to input', () => {
|
||||
sandbox.stub(element.$.restAPI, 'getSuggestedGroups', () =>
|
||||
Promise.resolve({
|
||||
Polygerrit: 0,
|
||||
@@ -135,39 +124,26 @@ limitations under the License.
|
||||
gerrittest: 0,
|
||||
})
|
||||
);
|
||||
element._fetchGroups('ownerin', 'gerrit').then(s => {
|
||||
assert.equal(s[0], 'ownerin:Polygerrit');
|
||||
assert.equal(s[1], 'ownerin:gerrit');
|
||||
assert.equal(s[2], 'ownerin:gerrittest');
|
||||
done();
|
||||
return element._fetchGroups('ownerin', 'gerrit').then(s => {
|
||||
assert.deepEqual(s[0], {text: 'ownerin:Polygerrit'});
|
||||
assert.deepEqual(s[1], {text: 'ownerin:gerrit'});
|
||||
assert.deepEqual(s[2], {text: 'ownerin:gerrittest'});
|
||||
});
|
||||
});
|
||||
|
||||
test('Autocompletes accounts with no email', done => {
|
||||
test('Autocompletes accounts with no email', () => {
|
||||
sandbox.stub(element.$.restAPI, 'getSuggestedAccounts', () =>
|
||||
Promise.resolve([
|
||||
{
|
||||
name: 'fred',
|
||||
},
|
||||
])
|
||||
);
|
||||
element._fetchAccounts('owner', 'fr').then(s => {
|
||||
assert.equal(s[0], 'owner:"fred"');
|
||||
done();
|
||||
Promise.resolve([{name: 'fred'}]));
|
||||
return element._fetchAccounts('owner', 'fr').then(s => {
|
||||
assert.deepEqual(s[0], {text: 'owner:"fred"', label: 'fred'});
|
||||
});
|
||||
});
|
||||
|
||||
test('Autocompletes accounts with email', done => {
|
||||
test('Autocompletes accounts with email', () => {
|
||||
sandbox.stub(element.$.restAPI, 'getSuggestedAccounts', () =>
|
||||
Promise.resolve([
|
||||
{
|
||||
email: 'fred@goog.co',
|
||||
},
|
||||
])
|
||||
);
|
||||
element._fetchAccounts('owner', 'fr').then(s => {
|
||||
assert.equal(s[0], 'owner:fred@goog.co');
|
||||
done();
|
||||
Promise.resolve([{email: 'fred@goog.co'}]));
|
||||
return element._fetchAccounts('owner', 'fr').then(s => {
|
||||
assert.deepEqual(s[0], {text: 'owner:fred@goog.co', label: ''});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -47,10 +47,11 @@
|
||||
/**
|
||||
* Query for requesting autocomplete suggestions. The function should
|
||||
* accept the input as a string parameter and return a promise. The
|
||||
* promise should yield an array of suggestion objects with "name" and
|
||||
* promise yields an array of suggestion objects with "name", "label",
|
||||
* "value" properties. The "name" property will be displayed in the
|
||||
* suggestion entry. The "value" property will be emitted if that
|
||||
* suggestion is selected.
|
||||
* suggestion entry. The "label" property will, when specified, appear
|
||||
* next to the "name" as label text. The "value" property will be emitted
|
||||
* if that suggestion is selected.
|
||||
*
|
||||
* @type {function(string): Promise<?>}
|
||||
*/
|
||||
|
Reference in New Issue
Block a user