Show user information above search results when searching for an owner
Change-Id: Ib4beb40ad9a24500bbd81f35aef244b12aa868ac
This commit is contained in:
@@ -20,6 +20,7 @@ limitations under the License.
|
||||
<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
|
||||
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
|
||||
<link rel="import" href="../gr-change-list/gr-change-list.html">
|
||||
<link rel="import" href="../gr-user-header/gr-user-header.html">
|
||||
<link rel="import" href="../../../styles/shared-styles.html">
|
||||
|
||||
<dom-module id="gr-change-list-view">
|
||||
@@ -46,6 +47,9 @@ limitations under the License.
|
||||
nav a:first-of-type {
|
||||
margin-right: .5em;
|
||||
}
|
||||
.hide {
|
||||
display: none;
|
||||
}
|
||||
@media only screen and (max-width: 50em) {
|
||||
.loading,
|
||||
.error {
|
||||
@@ -55,6 +59,9 @@ limitations under the License.
|
||||
</style>
|
||||
<div class="loading" hidden$="[[!_loading]]" hidden>Loading...</div>
|
||||
<div hidden$="[[_loading]]" hidden>
|
||||
<gr-user-header
|
||||
user-id="[[_userId]]"
|
||||
class$="[[_computeUserHeaderClass(_userId)]]"></gr-user-header>
|
||||
<gr-change-list
|
||||
changes="{{_changes}}"
|
||||
selected-index="{{viewState.selectedChangeIndex}}"
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
CHANGE_NUM: /^\s*[1-9][0-9]*\s*$/g,
|
||||
};
|
||||
|
||||
const USER_QUERY_PATTERN = /^owner:\s?("[^"]+"|[^ ]+)$/;
|
||||
|
||||
const LIMIT_OPERATOR_PATTERN = /\blimit:(\d+)/i;
|
||||
|
||||
Polymer({
|
||||
@@ -84,7 +86,10 @@
|
||||
/**
|
||||
* Change objects loaded from the server.
|
||||
*/
|
||||
_changes: Array,
|
||||
_changes: {
|
||||
type: Array,
|
||||
observer: '_changesChanged',
|
||||
},
|
||||
|
||||
/**
|
||||
* For showing a "loading..." string during ajax requests.
|
||||
@@ -93,6 +98,12 @@
|
||||
type: Boolean,
|
||||
value: true,
|
||||
},
|
||||
|
||||
/** @type {?String} */
|
||||
_userId: {
|
||||
type: String,
|
||||
value: null,
|
||||
},
|
||||
},
|
||||
|
||||
listeners: {
|
||||
@@ -188,5 +199,18 @@
|
||||
page.show(this._computeNavLink(
|
||||
this._query, this._offset, -1, this._changesPerPage));
|
||||
},
|
||||
|
||||
_changesChanged(changes) {
|
||||
if (!changes || !changes.length ||
|
||||
!USER_QUERY_PATTERN.test(this._query)) {
|
||||
this._userId = null;
|
||||
return;
|
||||
}
|
||||
this._userId = changes[0].owner.email;
|
||||
},
|
||||
|
||||
_computeUserHeaderClass(userId) {
|
||||
return userId ? '' : 'hide';
|
||||
},
|
||||
});
|
||||
})();
|
||||
|
||||
@@ -167,6 +167,21 @@ limitations under the License.
|
||||
assert.isTrue(showStub.called);
|
||||
});
|
||||
|
||||
test('_userId query', done => {
|
||||
assert.isNull(element._userId);
|
||||
element._query = 'owner: foo@bar';
|
||||
element._changes = [{owner: {email: 'foo@bar'}}];
|
||||
flush(() => {
|
||||
assert.equal(element._userId, 'foo@bar');
|
||||
|
||||
element._query = 'foo bar baz';
|
||||
element._changes = [{owner: {email: 'foo@bar'}}];
|
||||
assert.isNull(element._userId);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
suite('query based navigation', () => {
|
||||
test('Searching for a change ID redirects to change', done => {
|
||||
sandbox.stub(element, '_getChanges')
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
<!--
|
||||
Copyright (C) 2017 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-avatar/gr-avatar.html">
|
||||
<link rel="import" href="../../shared/gr-date-formatter/gr-date-formatter.html">
|
||||
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
|
||||
<link rel="import" href="../../../styles/shared-styles.html">
|
||||
|
||||
<dom-module id="gr-user-header">
|
||||
<template>
|
||||
<style include="shared-styles">
|
||||
:host {
|
||||
display: block;
|
||||
height: 9em;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
gr-avatar {
|
||||
height: 7em;
|
||||
left: 1em;
|
||||
position: absolute;
|
||||
top: 1em;
|
||||
width: 7em;
|
||||
}
|
||||
.info {
|
||||
left: 9em;
|
||||
position: absolute;
|
||||
top: 1em;
|
||||
}
|
||||
.info > div > span {
|
||||
display: inline-block;
|
||||
font-weight: bold;
|
||||
text-align: right;
|
||||
width: 6em;
|
||||
}
|
||||
.name {
|
||||
margin-bottom: .25em;
|
||||
}
|
||||
.name hr {
|
||||
width: 100%;
|
||||
}
|
||||
.status.hide,
|
||||
.name.hide {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<gr-avatar
|
||||
account="[[_accountDetails]]"
|
||||
image-size="100"
|
||||
aria-label="Account avatar"></gr-avatar>
|
||||
<div class="info">
|
||||
<h1 class$="name">
|
||||
[[_computeDetail(_accountDetails, 'name')]]
|
||||
<hr/>
|
||||
</h1>
|
||||
<div class$="status [[_computeStatusClass(_accountDetails)]]">
|
||||
<span>Status:</span> [[_status]]
|
||||
</div>
|
||||
<div>
|
||||
<span>Email:</span>
|
||||
<a href="mailto:[[_computeDetail(_accountDetails, 'email')]]"><!--
|
||||
-->[[_computeDetail(_accountDetails, 'email')]]</a>
|
||||
</div>
|
||||
<div>
|
||||
<span>Joined:</span>
|
||||
<gr-date-formatter
|
||||
date-str="[[_computeDetail(_accountDetails, 'registered_on')]]">
|
||||
</gr-date-formatter>
|
||||
</div>
|
||||
</div>
|
||||
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
|
||||
</template>
|
||||
<script src="gr-user-header.js"></script>
|
||||
</dom-module>
|
||||
@@ -0,0 +1,68 @@
|
||||
// Copyright (C) 2017 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-user-header',
|
||||
properties: {
|
||||
/** @type {?String} */
|
||||
userId: {
|
||||
type: String,
|
||||
observer: '_accountChanged',
|
||||
},
|
||||
|
||||
/**
|
||||
* @type {?{name: ?, email: ?, registered_on: ?}}
|
||||
*/
|
||||
_accountDetails: {
|
||||
type: Object,
|
||||
value: null,
|
||||
},
|
||||
|
||||
/** @type {?String} */
|
||||
_status: {
|
||||
type: String,
|
||||
value: null,
|
||||
},
|
||||
},
|
||||
|
||||
_accountChanged(userId) {
|
||||
if (!userId) {
|
||||
this._accountDetails = null;
|
||||
this._status = null;
|
||||
return;
|
||||
}
|
||||
|
||||
this.$.restAPI.getAccountDetails(userId).then(details => {
|
||||
this._accountDetails = details;
|
||||
});
|
||||
this.$.restAPI.getAccountStatus(userId).then(status => {
|
||||
this._status = status;
|
||||
});
|
||||
},
|
||||
|
||||
_computeDisplayClass(status) {
|
||||
return status ? ' ' : 'hide';
|
||||
},
|
||||
|
||||
_computeDetail(accountDetails, name) {
|
||||
return accountDetails ? accountDetails[name] : '';
|
||||
},
|
||||
|
||||
_computeStatusClass(accountDetails) {
|
||||
return this._computeDetail(accountDetails, 'status') ? '' : 'hide';
|
||||
},
|
||||
});
|
||||
})();
|
||||
@@ -0,0 +1,72 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
Copyright (C) 2017 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-user-header</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-user-header.html">
|
||||
|
||||
<script>void(0);</script>
|
||||
|
||||
<test-fixture id="basic">
|
||||
<template>
|
||||
<gr-user-header></gr-user-header>
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
<script>
|
||||
suite('gr-user-header tests', () => {
|
||||
let element;
|
||||
let sandbox;
|
||||
|
||||
setup(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
element = fixture('basic');
|
||||
});
|
||||
|
||||
teardown(() => { sandbox.restore(); });
|
||||
|
||||
test('loads and clears account info', done => {
|
||||
sandbox.stub(element.$.restAPI, 'getAccountDetails')
|
||||
.returns(Promise.resolve({
|
||||
name: 'foo',
|
||||
email: 'bar',
|
||||
registered_on: '2015-03-12 18:32:08.000000000',
|
||||
}));
|
||||
sandbox.stub(element.$.restAPI, 'getAccountStatus')
|
||||
.returns(Promise.resolve('baz'));
|
||||
|
||||
element.userId = 'foo.bar@baz';
|
||||
flush(() => {
|
||||
assert.isOk(element._accountDetails);
|
||||
assert.isOk(element._status);
|
||||
|
||||
element.userId = null;
|
||||
flush(() => {
|
||||
flushAsynchronousOperations();
|
||||
assert.isNull(element._accountDetails);
|
||||
assert.isNull(element._status);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -486,6 +486,14 @@
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {string} userId the ID of the user usch as an email address.
|
||||
* @return {!Promise<!Object>}
|
||||
*/
|
||||
getAccountDetails(userId) {
|
||||
return this.fetchJSON(`/accounts/${encodeURIComponent(userId)}/detail`);
|
||||
},
|
||||
|
||||
getAccountEmails() {
|
||||
return this._fetchSharedCacheURL('/accounts/self/emails');
|
||||
},
|
||||
@@ -579,6 +587,10 @@
|
||||
});
|
||||
},
|
||||
|
||||
getAccountStatus(userId) {
|
||||
return this.fetchJSON(`/accounts/${encodeURIComponent(userId)}/status`);
|
||||
},
|
||||
|
||||
getAccountGroups() {
|
||||
return this._fetchSharedCacheURL('/accounts/self/groups');
|
||||
},
|
||||
|
||||
@@ -52,6 +52,7 @@ limitations under the License.
|
||||
'change-list/gr-change-list-item/gr-change-list-item_test.html',
|
||||
'change-list/gr-change-list-view/gr-change-list-view_test.html',
|
||||
'change-list/gr-change-list/gr-change-list_test.html',
|
||||
'change-list/gr-user-header/gr-user-header_test.html',
|
||||
'change/gr-account-entry/gr-account-entry_test.html',
|
||||
'change/gr-account-list/gr-account-list_test.html',
|
||||
'change/gr-change-actions/gr-change-actions_test.html',
|
||||
|
||||
Reference in New Issue
Block a user