Show group memberships in the PolyGerrit settings screen

Introduces gr-group-list for the user-group display, which is added to
the gr-settings-view. Also refactors some table styles.

Bug: Issue 3911
Change-Id: I41748e16a98f437c6931caa1e43b69f01d801ea5
This commit is contained in:
Wyatt Allen
2016-06-16 14:53:01 -07:00
parent 767b76015b
commit 6405650742
12 changed files with 365 additions and 172 deletions

View File

@@ -44,38 +44,41 @@ limitations under the License.
border: 1px solid #ddd;
}
</style>
<table>
<thead>
<tr>
<th class="emailHeader">Email</th>
<th class="preferredHeader">Preferred</th>
<th></th>
</tr>
</thead>
<tbody>
<template is="dom-repeat" items="[[_emails]]">
<style include="gr-settings-styles"></style>
<div class="gr-settings-styles">
<table>
<thead>
<tr>
<td>[[item.email]]</td>
<td class="preferredControl" on-tap="_handlePreferredControlTap">
<input
is="iron-input"
type="radio"
on-change="_handlePreferredChange"
name="preferred"
value="[[item.email]]"
checked$="[[item.preferred]]">
</td>
<td>
<gr-button
data-index$="[[index]]"
on-tap="_handleDeleteButton"
disabled="[[item.preferred]]"
class="remove-button">Delete</gr-button>
</td>
<th class="emailHeader">Email</th>
<th class="preferredHeader">Preferred</th>
<th></th>
</tr>
</template>
</tbody>
</table>
</thead>
<tbody>
<template is="dom-repeat" items="[[_emails]]">
<tr>
<td>[[item.email]]</td>
<td class="preferredControl" on-tap="_handlePreferredControlTap">
<input
is="iron-input"
type="radio"
on-change="_handlePreferredChange"
name="preferred"
value="[[item.email]]"
checked$="[[item.preferred]]">
</td>
<td>
<gr-button
data-index$="[[index]]"
on-tap="_handleDeleteButton"
disabled="[[item.preferred]]"
class="remove-button">Delete</gr-button>
</td>
</tr>
</template>
</tbody>
</table>
</div>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
</template>
<script src="gr-email-editor.js"></script>

View File

@@ -0,0 +1,59 @@
<!--
Copyright (C) 2016 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-rest-api-interface/gr-rest-api-interface.html">
<link rel="import" href="../../../styles/gr-settings-styles.html">
<dom-module id="gr-group-list">
<template>
<style>
.nameHeader {
width: 15em;
}
.descriptionHeader {
width: 21.5em;
}
.visibleCell {
text-align: center;
}
</style>
<style include="gr-settings-styles"></style>
<div class="gr-settings-styles">
<table>
<thead>
<tr>
<th class="nameHeader">Name</th>
<th class="descriptionHeader">Description</th>
<th>Visible to All</th>
</tr>
</thead>
<tbody>
<template is="dom-repeat" items="[[_groups]]">
<tr>
<td>[[item.name]]</td>
<td>[[item.description]]</td>
<td class="visibleCell">[[_computeVisibleToAll(item)]]</td>
</tr>
</template>
</tbody>
</table>
</div>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
</template>
<script src="gr-group-list.js"></script>
</dom-module>

View File

@@ -0,0 +1,36 @@
// Copyright (C) 2016 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-group-list',
properties: {
_groups: Array,
},
loadData: function() {
return this.$.restAPI.getAccountGroups().then(function(groups) {
this._groups = groups.sort(function(a, b) {
return a.name.localeCompare(b.name);
});
}.bind(this));
},
_computeVisibleToAll: function(group) {
return group.options.visible_to_all ? 'Yes' : 'No';
},
});
})();

View File

@@ -0,0 +1,84 @@
<!DOCTYPE html>
<!--
Copyright (C) 2016 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-settings-view</title>
<script src="../../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
<script src="../../../bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="gr-group-list.html">
<test-fixture id="basic">
<template>
<gr-group-list></gr-group-list>
</template>
</test-fixture>
<script>
suite('gr-group-list tests', function() {
var element;
var groups;
setup(function(done) {
groups = [{
url: 'some url',
options: {},
description: 'Group 1 description',
group_id: 1,
owner: 'Administrators',
owner_id: '123',
id: 'abc',
name: 'Group 1',
},{
options: {visible_to_all: true},
id: '456',
name: 'Group 2',
},{
options: {},
id: '789',
name: 'Group 3',
}];
stub('gr-rest-api-interface', {
getAccountGroups: function() { return Promise.resolve(groups); },
});
element = fixture('basic');
element.loadData().then(function() { flush(done); });
});
test('renders', function() {
var rows = Polymer.dom(element.root).querySelectorAll('tbody tr');
assert.equal(rows.length, 3);
var nameCells = rows.map(
function(row) { return row.querySelectorAll('td')[0].textContent; });
assert.equal(nameCells[0], 'Group 1');
assert.equal(nameCells[1], 'Group 2');
assert.equal(nameCells[2], 'Group 3');
});
test('_computeVisibleToAll', function() {
assert.equal(element._computeVisibleToAll(groups[0]), 'No');
assert.equal(element._computeVisibleToAll(groups[1]), 'Yes');
});
});
</script>

View File

@@ -19,28 +19,19 @@ limitations under the License.
<link rel="import" href="../../shared/gr-button/gr-button.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="../../shared/gr-select/gr-select.html">
<link rel="import" href="../../../styles/gr-settings-styles.html">
<dom-module id="gr-menu-editor">
<template>
<style>
th {
color: #666;
text-align: left;
}
th.nameHeader {
width: 11em;
}
tbody tr:nth-child(even) {
background-color: #f4f4f4;
}
tbody tr:first-of-type td .move-up-button,
tbody tr:last-of-type td .move-down-button {
display: none;
}
input {
font-size: 1em;
}
.newTitleInput {
width: 10em;
}
@@ -48,67 +39,70 @@ limitations under the License.
width: 23em;
}
</style>
<table>
<thead>
<tr>
<th class="nameHeader">Name</th>
<th class="url-header">URL</th>
</tr>
</thead>
<tbody>
<template is="dom-repeat" items="[[menuItems]]">
<style include="gr-settings-styles"></style>
<div class="gr-settings-styles">
<table>
<thead>
<tr>
<td>[[item.name]]</td>
<td>[[item.url]]</td>
<td>
<gr-button
data-index="[[index]]"
on-tap="_handleMoveUpButton"
class="move-up-button"></gr-button>
</td>
<td>
<gr-button
data-index="[[index]]"
on-tap="_handleMoveDownButton"
class="move-down-button"></gr-button>
</td>
<td>
<gr-button
data-index="[[index]]"
on-tap="_handleDeleteButton"
class="remove-button">Delete</gr-button>
</td>
<th class="nameHeader">Name</th>
<th class="url-header">URL</th>
</tr>
</template>
</tbody>
<tfoot>
<tr>
<th>
<input
class="newTitleInput"
is="iron-input"
placeholder="New Title"
on-keydown="_handleInputKeydown"
bind-value="{{_newName}}">
</th>
<th>
<input
class="newUrlInput"
is="iron-input"
placeholder="New URL"
on-keydown="_handleInputKeydown"
bind-value="{{_newUrl}}">
</th>
<th></th>
<th></th>
<th>
<gr-button
disabled$="[[_computeAddDisabled(_newName, _newUrl)]]"
on-tap="_handleAddButton">Add</gr-button>
</th>
</tr>
</tfoot>
</table>
</thead>
<tbody>
<template is="dom-repeat" items="[[menuItems]]">
<tr>
<td>[[item.name]]</td>
<td>[[item.url]]</td>
<td>
<gr-button
data-index="[[index]]"
on-tap="_handleMoveUpButton"
class="move-up-button"></gr-button>
</td>
<td>
<gr-button
data-index="[[index]]"
on-tap="_handleMoveDownButton"
class="move-down-button"></gr-button>
</td>
<td>
<gr-button
data-index="[[index]]"
on-tap="_handleDeleteButton"
class="remove-button">Delete</gr-button>
</td>
</tr>
</template>
</tbody>
<tfoot>
<tr>
<th>
<input
class="newTitleInput"
is="iron-input"
placeholder="New Title"
on-keydown="_handleInputKeydown"
bind-value="{{_newName}}">
</th>
<th>
<input
class="newUrlInput"
is="iron-input"
placeholder="New URL"
on-keydown="_handleInputKeydown"
bind-value="{{_newUrl}}">
</th>
<th></th>
<th></th>
<th>
<gr-button
disabled$="[[_computeAddDisabled(_newName, _newUrl)]]"
on-tap="_handleAddButton">Add</gr-button>
</th>
</tr>
</tfoot>
</table>
</div>
</template>
<script src="gr-menu-editor.js"></script>
</dom-module>

View File

@@ -18,6 +18,7 @@ limitations under the License.
<link rel="import" href="../gr-account-info/gr-account-info.html">
<link rel="import" href="../gr-email-editor/gr-email-editor.html">
<link rel="import" href="../gr-group-list/gr-group-list.html">
<link rel="import" href="../gr-menu-editor/gr-menu-editor.html">
<link rel="import" href="../gr-watched-projects-editor/gr-watched-projects-editor.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
@@ -255,6 +256,10 @@ limitations under the License.
disabled="[[!_computeAddEmailButtonEnabled(_newEmail, _addingEmail)]]"
on-tap="_handleAddEmailButton">Send Verification</gr-button>
</fieldset>
<h2>Groups</h2>
<fieldset>
<gr-group-list id="groupList"></gr-group-list>
</fieldset>
</main>
</div>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>

View File

@@ -105,6 +105,8 @@
promises.push(this.$.emailEditor.loadData());
promises.push(this.$.groupList.loadData());
Promise.all(promises).then(function() {
this._loading = false;
}.bind(this));

View File

@@ -118,6 +118,7 @@ limitations under the License.
},
getAccountEmails: function() { return Promise.resolve(); },
getConfig: function() { return Promise.resolve(config); },
getAccountGroups: function() { return Promise.resolve([]); },
});
element = fixture('basic');

View File

@@ -17,14 +17,11 @@ limitations under the License.
<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-rest-api-interface/gr-rest-api-interface.html">
<link rel="import" href="../../../styles/gr-settings-styles.html">
<dom-module id="gr-watched-projects-editor">
<template>
<style>
th {
color: #666;
text-align: left;
}
th.projectHeader {
width: 11em;
}
@@ -35,9 +32,6 @@ limitations under the License.
text-align: center;
padding: 0 0.4em;
}
tbody tr:nth-child(even) {
background-color: #f4f4f4;
}
td.notifControl {
cursor: pointer;
text-align: center;
@@ -60,74 +54,77 @@ limitations under the License.
width: 26em;
}
</style>
<table>
<thead>
<tr>
<th class="projectHeader">Project</th>
<template is="dom-repeat" items="[[_getTypes()]]">
<th class="notifType">[[item.name]]</th>
</template>
<th></th>
</tr>
</thead>
<tbody>
<template
is="dom-repeat"
items="[[_projects]]"
as="project"
index-as="projectIndex">
<style include="gr-settings-styles"></style>
<div class="gr-settings-styles">
<table>
<thead>
<tr>
<td>
[[project.project]]
<template is="dom-if" if="[[project.filter]]">
<div class="projectFilter">[[project.filter]]</div>
</template>
</td>
<template
is="dom-repeat"
items="[[_getTypes()]]"
as="type">
<td class="notifControl" on-tap="_handleNotifCellTap">
<input
type="checkbox"
data-index$="[[projectIndex]]"
data-key$="[[type.key]]"
on-change="_handleCheckboxChange"
checked$="[[_computeCheckboxChecked(project, type.key)]]">
</td>
<th class="projectHeader">Project</th>
<template is="dom-repeat" items="[[_getTypes()]]">
<th class="notifType">[[item.name]]</th>
</template>
<td class="delete-column">
<gr-button
data-index$="[[projectIndex]]"
on-tap="_handleRemoveProject">Delete</gr-button>
</td>
<th></th>
</tr>
</template>
</tbody>
<tfoot>
<tr>
<th>
<gr-autocomplete
id="newProject"
class="newProjectInput"
is="iron-input"
query="[[_query]]"
threshold="3"
placeholder="Project"></gr-autocomplete>
</th>
<th colspan$="[[_getTypeCount()]]">
<input
id="newFilter"
class="newFilterInput"
is="iron-input"
placeholder="branch:name, or other search expression">
</th>
<th>
<gr-button on-tap="_handleAddProject">Add</gr-button>
</th>
</tr>
</tfoot>
</table>
</thead>
<tbody>
<template
is="dom-repeat"
items="[[_projects]]"
as="project"
index-as="projectIndex">
<tr>
<td>
[[project.project]]
<template is="dom-if" if="[[project.filter]]">
<div class="projectFilter">[[project.filter]]</div>
</template>
</td>
<template
is="dom-repeat"
items="[[_getTypes()]]"
as="type">
<td class="notifControl" on-tap="_handleNotifCellTap">
<input
type="checkbox"
data-index$="[[projectIndex]]"
data-key$="[[type.key]]"
on-change="_handleCheckboxChange"
checked$="[[_computeCheckboxChecked(project, type.key)]]">
</td>
</template>
<td class="delete-column">
<gr-button
data-index$="[[projectIndex]]"
on-tap="_handleRemoveProject">Delete</gr-button>
</td>
</tr>
</template>
</tbody>
<tfoot>
<tr>
<th>
<gr-autocomplete
id="newProject"
class="newProjectInput"
is="iron-input"
query="[[_query]]"
threshold="3"
placeholder="Project"></gr-autocomplete>
</th>
<th colspan$="[[_getTypeCount()]]">
<input
id="newFilter"
class="newFilterInput"
is="iron-input"
placeholder="branch:name, or other search expression">
</th>
<th>
<gr-button on-tap="_handleAddProject">Add</gr-button>
</th>
</tr>
</tfoot>
</table>
</div>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
</template>
<script src="gr-watched-projects-editor.js"></script>

View File

@@ -240,6 +240,10 @@
opt_ctx);
},
getAccountGroups: function() {
return this._fetchSharedCacheURL('/accounts/self/groups');
},
getLoggedIn: function() {
return this.getAccount().then(function(account) {
return account != null;

View File

@@ -37,6 +37,13 @@ limitations under the License.
.gr-settings-styles input {
font-size: 1em;
}
.gr-settings-styles th {
color: #666;
text-align: left;
}
.gr-settings-styles tbody tr:nth-child(even) {
background-color: #f4f4f4;
}
@media only screen and (max-width: 40em) {
.gr-settings-styles section {
margin-bottom: 1em;

View File

@@ -56,6 +56,7 @@ limitations under the License.
'diff/gr-selection-action-box/gr-selection-action-box_test.html',
'settings/gr-account-info/gr-account-info_test.html',
'settings/gr-email-editor/gr-email-editor_test.html',
'settings/gr-group-list/gr-group-list_test.html',
'settings/gr-menu-editor/gr-menu-editor_test.html',
'settings/gr-settings-view/gr-settings-view_test.html',
'settings/gr-watched-projects-editor/gr-watched-projects-editor_test.html',