Add setting to Show/Hide change table columns in settings

This change adds a widget to adjust change table columns in the user's
settings. If no change table columns are adjusted, the server returns an
empty array and the client determines the default columns, stored in the
gr-change-table-behavior.

Columns can get removed/re-ordered by the user but they are limited to
those specified in gr-change-table-behavior. Column preferences persist
across all gr-change-list items (dashboards, searches, etc).

Feature: Issue 4753
Change-Id: I47d5b9f53a95c0c010b04c4495094f188d85e67e
This commit is contained in:
Becky Siegel
2016-11-14 08:37:06 -08:00
parent 0e93b82694
commit 1a44b34247
14 changed files with 852 additions and 60 deletions

View File

@@ -0,0 +1,50 @@
<!--
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.
-->
<script>
(function(window) {
'use strict';
/** @polymerBehavior Gerrit.ChangeTableBehavior */
var ChangeTableBehavior = {
CHANGE_TABLE_COLUMNS: [
'Subject',
'Status',
'Owner',
'Project',
'Branch',
'Updated',
'Size',
],
/**
* Returns the complement to the given column array
* @param {Array} columns
*/
getComplementColumns: function(columns) {
return this.CHANGE_TABLE_COLUMNS.filter(function(column) {
return columns.indexOf(column) === -1;
});
},
isColumnHidden: function(columnToCheck, columnsToDisplay) {
return columnsToDisplay.indexOf(columnToCheck) === -1;
},
};
window.Gerrit = window.Gerrit || {};
window.Gerrit.ChangeTableBehavior = ChangeTableBehavior;
})(window);
</script>

View File

@@ -0,0 +1,106 @@
<!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>keyboard-shortcut-behavior</title>
<script src="../../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
<script src="../../../bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
<link rel="import" href="gr-change-table-behavior.html">
<test-fixture id="basic">
<template>
<test-element></test-element>
</template>
</test-fixture>
<test-fixture id="within-overlay">
<template>
<gr-overlay>
<test-element></test-element>
</gr-overlay>
</template>
</test-fixture>
<script>
suite('gr-change-table-behavior tests', function() {
var element;
var overlay;
suiteSetup(function() {
// Define a Polymer element that uses this behavior.
Polymer({
is: 'test-element',
behaviors: [Gerrit.ChangeTableBehavior],
});
});
setup(function() {
element = fixture('basic');
overlay = fixture('within-overlay');
});
test('getComplementColumns', function() {
var columns = [
'Subject',
'Status',
'Owner',
'Project',
'Branch',
'Updated',
'Size',
];
assert.deepEqual(element.getComplementColumns(columns), []);
columns = [
'Subject',
'Status',
'Project',
'Branch',
'Size',
];
assert.deepEqual(element.getComplementColumns(columns),
['Owner', 'Updated']);
});
test('isColumnHidden', function() {
var columnToCheck = 'Project';
var columnsToDisplay = [
'Subject',
'Status',
'Owner',
'Project',
'Branch',
'Updated',
'Size',
];
assert.isFalse(element.isColumnHidden(columnToCheck, columnsToDisplay));
var columnsToDisplay = [
'Subject',
'Status',
'Owner',
'Branch',
'Updated',
'Size',
];
assert.isTrue(element.isColumnHidden(columnToCheck, columnsToDisplay));
});
});
</script>

View File

@@ -13,7 +13,7 @@ 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="../../../behaviors/gr-change-table-behavior/gr-change-table-behavior.html">
<link rel="import" href="../../../behaviors/gr-url-encoding-behavior.html">
<link rel="import" href="../../../behaviors/rest-client-behavior.html">
<link rel="import" href="../../../bower_components/polymer/polymer.html">
@@ -66,30 +66,51 @@ limitations under the License.
}
</style>
<style include="gr-change-list-styles"></style>
<span class="cell keyboard">
<td class="cell keyboard">
<span class="positionIndicator">&#x25b6;</span>
</span>
<span class="cell star" hidden$="[[!showStar]]" hidden>
</td>
<td class="cell star" hidden$="[[!showStar]]" hidden>
<gr-change-star change="{{change}}"></gr-change-star>
</span>
<a class="cell number" href$="[[changeURL]]" hidden$="[[!showNumber]]" hidden>
[[change._number]]
</a>
<a class="cell subject" href$="[[changeURL]]">[[change.subject]]</a>
<span class="cell status">[[changeStatusString(change)]]</span>
<span class="cell owner">
</td>
<td class="cell number" hidden$="[[!showNumber]]" hidden>
<a href$="[[changeURL]]"> [[change._number]]</a>
</td>
<td class="cell subject"
hidden$="[[isColumnHidden('Subject', visibleChangeTableColumns)]]">
<a href$="[[changeURL]]">[[change.subject]]</a>
</td>
<td class="cell status"
hidden$="[[isColumnHidden('Status', visibleChangeTableColumns)]]">
[[changeStatusString(change)]]
</td>
<td class="cell owner"
hidden$="[[isColumnHidden('Owner', visibleChangeTableColumns)]]">
<gr-account-link account="[[change.owner]]"></gr-account-link>
</span>
<a class="cell project" href$="[[_computeProjectURL(change.project)]]">[[change.project]]</a>
<a class="cell branch" href$="[[_computeProjectBranchURL(change.project, change.branch)]]">[[change.branch]]</a>
<gr-date-formatter class="cell updated" date-str="[[change.updated]]"></gr-date-formatter>
<span class="cell size u-monospace">
</td>
<td class="cell project"
hidden$="[[isColumnHidden('Project', visibleChangeTableColumns)]]">
<a href$="[[_computeProjectURL(change.project)]]">[[change.project]]</a>
</td>
<td class="cell branch"
hidden$="[[isColumnHidden('Branch', visibleChangeTableColumns)]]">
<a href$="[[_computeProjectBranchURL(change.project, change.branch)]]">
[[change.branch]]
</a>
</td>
<td class="cell updated"
hidden$="[[isColumnHidden('Updated', visibleChangeTableColumns)]]">
<gr-date-formatter date-str="[[change.updated]]"></gr-date-formatter>
</td>
<td class="cell size u-monospace"
hidden$="[[isColumnHidden('Size', visibleChangeTableColumns)]]">
<span class="u-green"><span>+</span>[[change.insertions]]</span>,
<span class="u-red"><span>-</span>[[change.deletions]]</span>
</span>
</td>
<template is="dom-repeat" items="[[labelNames]]" as="labelName">
<span title$="[[_computeLabelTitle(change, labelName)]]"
class$="[[_computeLabelClass(change, labelName)]]">[[_computeLabelValue(change, labelName)]]</span>
<td title$="[[_computeLabelTitle(change, labelName)]]"
class$="[[_computeLabelClass(change, labelName)]]">
[[_computeLabelValue(change, labelName)]]
</td>
</template>
</template>
<script src="gr-change-list-item.js"></script>

View File

@@ -18,6 +18,7 @@
is: 'gr-change-list-item',
properties: {
visibleChangeTableColumns: Array,
selected: {
type: Boolean,
value: false,
@@ -43,6 +44,7 @@
},
behaviors: [
Gerrit.ChangeTableBehavior,
Gerrit.RESTClientBehavior,
Gerrit.URLEncodingBehavior,
],

View File

@@ -22,6 +22,7 @@ limitations under the License.
<script src="../../../bower_components/web-component-tester/browser.js"></script>
<script src="../../../scripts/util.js"></script>
<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
<link rel="import" href="gr-change-list-item.html">
<test-fixture id="basic">
@@ -45,7 +46,7 @@ limitations under the License.
test('change status', function() {
var getStatusForChange = function(change) {
element.change = change;
return element.$$('.cell.status').textContent;
return element.$$('.cell.status').textContent.trim();
};
assert.equal(getStatusForChange({mergeable: true}), '');
@@ -137,5 +138,75 @@ limitations under the License.
assert.equal(element.changeURL, '/c/43/');
});
test('no hidden columns', function() {
element.visibleChangeTableColumns = [
'Subject',
'Status',
'Owner',
'Project',
'Branch',
'Updated',
'Size',
];
flushAsynchronousOperations();
element.CHANGE_TABLE_COLUMNS.forEach(function(column) {
var elementClass = '.' + column.toLowerCase();
assert.isFalse(element.$$(elementClass).hidden);
});
});
test('no hidden columns', function() {
element.visibleChangeTableColumns = [
'Subject',
'Status',
'Owner',
'Project',
'Branch',
'Updated',
'Size',
];
flushAsynchronousOperations();
element.CHANGE_TABLE_COLUMNS.forEach(function(column) {
var elementClass = '.' + column.toLowerCase();
assert.isFalse(element.$$(elementClass).hidden);
});
});
test('project column hidden', function() {
element.visibleChangeTableColumns = [
'Subject',
'Status',
'Owner',
'Branch',
'Updated',
'Size',
];
flushAsynchronousOperations();
element.CHANGE_TABLE_COLUMNS.forEach(function(column) {
var elementClass = '.' + column.toLowerCase();
if (column === 'Project') {
assert.isTrue(element.$$(elementClass).hidden);
} else {
assert.isFalse(element.$$(elementClass).hidden);
}
});
});
test('random column does not exist', function() {
element.visibleChangeTableColumns = [
'Bad',
];
flushAsynchronousOperations();
var elementClass = '.bad';
assert.isNotOk(element.$$(elementClass));
});
});
</script>

View File

@@ -15,6 +15,7 @@ limitations under the License.
-->
<link rel="import" href="../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/gr-change-table-behavior/gr-change-table-behavior.html">
<link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
<link rel="import" href="../../../behaviors/rest-client-behavior.html">
<link rel="import" href="../../../styles/gr-change-list-styles.html">
@@ -28,42 +29,60 @@ limitations under the License.
display: flex;
flex-direction: column;
}
#changeList {
border-collapse: collapse;
width: 100%;
}
.cell {
flex-shrink: 0;
padding: .3em .5em;
}
th {
text-align: left;
}
</style>
<style include="gr-change-list-styles"></style>
<div class="headerRow">
<span class="topHeader keyboard"></span> <!-- keyboard position indicator -->
<span class="topHeader star" hidden$="[[!showStar]]" hidden></span>
<span class="topHeader number" hidden$="[[!showNumber]]" hidden>#</span>
<span class="topHeader subject">Subject</span>
<span class="topHeader status">Status</span>
<span class="topHeader owner">Owner</span>
<span class="topHeader project">Project</span>
<span class="topHeader branch">Branch</span>
<span class="topHeader updated">Updated</span>
<span class="topHeader size">Size</span>
<template is="dom-repeat" items="[[labelNames]]" as="labelName">
<span class="topHeader label" title$="[[labelName]]">
[[_computeLabelShortcut(labelName)]]
</span>
<table id="changeList">
<tr class="headerRow">
<th class="topHeader keyboard"></th>
<th class="topHeader star" hidden$="[[!showStar]]" hidden></th>
<th class="topHeader number" hidden$="[[!showNumber]]" hidden>#</th>
<template is="dom-repeat" items="[[changeTableColumns]]" as="item">
<th class$="[[_lowerCase(item)]] topHeader"
hidden$="[[isColumnHidden(item, visibleChangeTableColumns)]]">
[[item]]
</th>
</template>
<template is="dom-repeat" items="[[labelNames]]" as="labelName">
<th class="topHeader label" title$="[[labelName]]">
[[_computeLabelShortcut(labelName)]]
</th>
</template>
</tr>
<template is="dom-repeat" items="[[groups]]" as="changeGroup"
index-as="groupIndex">
<template is="dom-if" if="[[_groupTitle(groupIndex)]]">
<tr class="groupHeader">
<td class="cell">[[_groupTitle(groupIndex)]]</td>
</tr>
</template>
<template is="dom-if" if="[[!changeGroup.length]]">
<tr class="noChanges"><td class="cell">No changes</td></tr>
</template>
<template is="dom-repeat" items="[[changeGroup]]" as="change">
<tr>
<gr-change-list-item
selected$="[[_computeItemSelected(index, groupIndex, selectedIndex)]]"
needs-review="[[_computeItemNeedsReview(account, change, showReviewedState)]]"
change="[[change]]"
visible-change-table-columns="[[visibleChangeTableColumns]]"
show-number="[[showNumber]]"
show-star="[[showStar]]"
label-names="[[labelNames]]"></gr-change-list-item>
</tr>
</template>
</template>
</div>
<template is="dom-repeat" items="{{groups}}" as="changeGroup" index-as="groupIndex">
<template is="dom-if" if="[[_groupTitle(groupIndex)]]">
<div class="groupHeader">[[_groupTitle(groupIndex)]]</div>
</template>
<template is="dom-if" if="[[!changeGroup.length]]">
<div class="noChanges">No changes</div>
</template>
<template is="dom-repeat" items="[[changeGroup]]" as="change">
<gr-change-list-item
selected$="[[_computeItemSelected(index, groupIndex, selectedIndex)]]"
needs-review="[[_computeItemNeedsReview(account, change, showReviewedState)]]"
change="[[change]]"
show-number="[[showNumber]]"
show-star="[[showStar]]"
label-names="[[labelNames]]"></gr-change-list-item>
</template>
</template>
</table>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
</template>
<script src="gr-change-list.js"></script>

View File

@@ -74,6 +74,7 @@
},
behaviors: [
Gerrit.ChangeTableBehavior,
Gerrit.KeyboardShortcutBehavior,
Gerrit.RESTClientBehavior,
],
@@ -88,15 +89,24 @@
this._loadPreferences();
},
_lowerCase: function(column) {
return column.toLowerCase();
},
_loadPreferences: function() {
return this._getLoggedIn().then(function(loggedIn) {
this.changeTableColumns = this.CHANGE_TABLE_COLUMNS;
if (!loggedIn) {
this.showNumber = false;
this.visibleChangeTableColumns = this.CHANGE_TABLE_COLUMNS;
return;
}
return this._getPreferences().then(function(preferences) {
this.showNumber = !!(preferences &&
preferences.legacycid_in_change_table);
this.visibleChangeTableColumns = preferences.change_table.length > 0 ?
preferences.change_table : this.CHANGE_TABLE_COLUMNS;
}.bind(this));
}.bind(this));
},

View File

@@ -70,9 +70,10 @@ limitations under the License.
suite('test show change number preference enabled', function() {
setup(function(done) {
return stubRestAPI(
{legacycid_in_change_table: true, time_format: 'HHMM_12'}
).then(function() {
return stubRestAPI({legacycid_in_change_table: true,
time_format: 'HHMM_12',
change_table: [],
}).then(function() {
element = fixture('basic');
element._loadPreferences().then(function() { done(); });
});
@@ -86,9 +87,8 @@ limitations under the License.
suite('test show change number preference disabled', function() {
setup(function(done) {
// legacycid_in_change_table is not set when false.
return stubRestAPI(
{time_format: 'HHMM_12'}
).then(function() {
return stubRestAPI({time_format: 'HHMM_12', change_table: []}).then(
function() {
element = fixture('basic');
element._loadPreferences().then(function() { done(); });
});
@@ -235,6 +235,120 @@ limitations under the License.
'.noChanges');
assert.equal(noChangesMsg.length, 2);
});
suite('empty column preference', function() {
var element;
setup(function(done) {
return stubRestAPI({
legacycid_in_change_table: true,
time_format: 'HHMM_12',
change_table: [],
}
).then(function() {
element = fixture('basic');
element._loadPreferences().then(function() { done(); });
});
});
test('show number enabled', function() {
assert.isTrue(element.showNumber);
});
test('all columns visible', function() {
element.CHANGE_TABLE_COLUMNS.forEach(function(column) {
var elementClass = '.' + element._lowerCase(column);
assert.isFalse(element.$$(elementClass).hidden);
});
});
});
suite('full column preference', function() {
var element;
setup(function(done) {
return stubRestAPI({
legacycid_in_change_table: true,
time_format: 'HHMM_12',
change_table: [
'Subject',
'Status',
'Owner',
'Project',
'Branch',
'Updated',
'Size',
],
}).then(function() {
element = fixture('basic');
element._loadPreferences().then(function() { done(); });
});
});
test('all columns visible', function() {
element.changeTableColumns.forEach(function(column) {
var elementClass = '.' + element._lowerCase(column);
assert.isFalse(element.$$(elementClass).hidden);
});
});
});
suite('partial column preference', function() {
var element;
setup(function(done) {
return stubRestAPI({
legacycid_in_change_table: true,
time_format: 'HHMM_12',
change_table: [
'Subject',
'Status',
'Owner',
'Branch',
'Updated',
'Size',
],
}).then(function() {
element = fixture('basic');
element._loadPreferences().then(function() { done(); });
});
});
test('all columns except project visible', function() {
element.changeTableColumns.forEach(function(column) {
var elementClass = '.' + column.toLowerCase();
if (column === 'Project') {
assert.isTrue(element.$$(elementClass).hidden);
} else {
assert.isFalse(element.$$(elementClass).hidden);
}
});
});
});
suite('random column does not exist', function() {
var element;
/* This would only exist if somebody manually updated the config
file. */
setup(function(done) {
return stubRestAPI({
legacycid_in_change_table: true,
time_format: 'HHMM_12',
change_table: [
'Bad',
],
}).then(function() {
element = fixture('basic');
element._loadPreferences().then(function() { done(); });
});
});
test('bad column does not exist', function() {
var elementClass = '.bad';
assert.isNotOk(element.$$(elementClass));
});
});
});
suite('gr-change-list groups', function() {
@@ -294,6 +408,5 @@ limitations under the License.
'Should navigate to /c/4/');
showStub.restore();
});
});
</script>

View File

@@ -0,0 +1,95 @@
<!--
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="../../../behaviors/gr-change-table-behavior/gr-change-table-behavior.html">
<link rel="import" href="../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
<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="../../../styles/gr-settings-styles.html">
<dom-module id="gr-change-table-editor">
<template>
<style>
th.nameHeader {
width: 11em;
}
tbody tr:first-of-type td .move-up-button,
tbody tr:last-of-type td .move-down-button {
display: none;
}
.newTitleInput {
width: 10em;
}
.newUrlInput {
width: 23em;
}
.addOptions {
margin-top: 1em;
}
</style>
<style include="gr-settings-styles"></style>
<div class="gr-settings-styles">
<table>
<thead>
<tr>
<th class="nameHeader">Column</th>
</tr>
</thead>
<tbody>
<template is="dom-repeat" items="[[changeTableItems]]">
<tr>
<td>[[item]]</td>
<td>
<gr-button
data-index="[[index]]"
on-tap="_handleDeleteButton"
class="remove-button">Delete</gr-button>
</td>
</tr>
</template>
</tbody>
</table>
<template is="dom-if" if="[[changeTableNotDisplayed.length]]">
<table class=addOptions>
<thead>
<tr>
<th class="nameHeader">Hidden</th>
</tr>
</thead>
<tbody>
<template is="dom-repeat" items="[[changeTableNotDisplayed]]">
<tr>
<td>[[item]]</td>
<td>
<gr-button
on-tap="_handleAddButton"
class="add-button"
data-index="[[index]]">
Add
</gr-button>
</td>
</tr>
</template>
<tbody>
</table>
</template>
</div>
</template>
<script src="gr-change-table-editor.js"></script>
</dom-module>

View File

@@ -0,0 +1,50 @@
// 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-change-table-editor',
properties: {
changeTableItems: Array,
changeTableNotDisplayed: Array,
},
behaviors: [
Gerrit.ChangeTableBehavior,
],
_handleDeleteButton: function(e) {
var index = e.target.dataIndex;
this.splice('changeTableItems', index, 1);
// Use the change table behavior to make sure ordering of unused
// columns ends up in the correct order. If the removed item is appended
// to the end, when it is saved, the unused column order may shift around.
this.set('changeTableNotDisplayed',
this.getComplementColumns(this.changeTableItems));
},
_handleAddButton: function(e) {
var index = e.target.dataIndex;
var newColumn = this.changeTableNotDisplayed[index];
this.splice('changeTableNotDisplayed', index, 1);
this.splice('changeTableItems', this.getComplementColumns(
this.changeTableNotDisplayed).indexOf(newColumn), 0, newColumn);
},
});
})();

View File

@@ -0,0 +1,193 @@
<!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="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
<link rel="import" href="gr-change-table-editor.html">
<test-fixture id="basic">
<template>
<gr-change-table-editor></gr-change-table-editor>
</template>
</test-fixture>
<script>
suite('gr-settings-view tests', function() {
var element;
var columns;
// Click the up/down button (according to direction) for the index'th row.
// The index of the first row is 0, corresponding to the array.
function move(element, index, direction) {
var selector =
'tr:nth-child(' + (index + 1) + ') .move-' + direction + '-button';
var button = element.$$('tbody').querySelector(selector);
MockInteractions.tap(button);
flushAsynchronousOperations();
}
setup(function() {
element = fixture('basic');
columns = [
'Subject',
'Status',
'Owner',
'Project',
'Branch',
'Updated',
];
columnsNotDisplayed = ['Size'];
element.set('changeTableItems', columns);
element.set('changeTableNotDisplayed', columnsNotDisplayed);
flushAsynchronousOperations();
});
test('renders', function() {
var rows = element.$$('tbody').querySelectorAll('tr');
var tds;
assert.equal(rows.length, columns.length);
for (var i = 0; i < columns.length; i++) {
tds = rows[i].querySelectorAll('td');
assert.equal(tds[0].textContent, columns[i]);
}
});
test('add hidden item', function() {
var originalNumberColumns = element.changeTableItems.length;
var originalNumberHiddenColumns = element.changeTableNotDisplayed.length;
var addBtn = element.$$('.addOptions gr-button');
var columnName = element.$$('.addOptions tr td').innerHTML;
assert.equal(element.$$('.addOptions').style.display, '');
MockInteractions.tap(addBtn);
flushAsynchronousOperations();
assert.equal(element.changeTableItems.length, originalNumberColumns + 1);
assert.equal(element.changeTableNotDisplayed.length,
originalNumberHiddenColumns - 1);
assert.equal(
element.changeTableItems[element.changeTableItems.length - 1],
columnName);
assert.equal(element.$$('.addOptions').style.display, 'none');
});
test('remove item', function() {
var columns = element.changeTableItems.length;
assert.deepEqual(element.changeTableItems, [
'Subject',
'Status',
'Owner',
'Project',
'Branch',
'Updated',
]);
// Tap the delete button for the second item.
MockInteractions.tap(
element.$$('tbody').querySelector('tr:nth-child(2) .remove-button'));
assert.deepEqual(element.changeTableItems, [
'Subject',
'Owner',
'Project',
'Branch',
'Updated',
]);
assert.deepEqual(element.changeTableNotDisplayed, [
'Status',
'Size',
]);
// Delete remaining items.
for (var i = 0; i < columns - 1; i++) {
MockInteractions.tap(
element.$$('tbody').querySelector('tr:first-child .remove-button'));
}
assert.deepEqual(element.changeTableItems, []);
assert.deepEqual(element.changeTableNotDisplayed, [
'Subject',
'Status',
'Owner',
'Project',
'Branch',
'Updated',
'Size',
]);
});
test('add item', function() {
element.set('changeTableItems', [
'Status',
'Owner',
'Project',
'Branch',
'Updated',
]);
element.set('changeTableNotDisplayed', ['Subject', 'Size']);
var columns = element.changeTableItems.length;
flushAsynchronousOperations();
// Tap the add button for the second item.
MockInteractions.tap(
element.$$('.addOptions').querySelector(
'tr:nth-child(2) .add-button'));
flushAsynchronousOperations();
assert.deepEqual(element.changeTableItems, [
'Status',
'Owner',
'Project',
'Branch',
'Updated',
'Size',
]);
assert.deepEqual(element.changeTableNotDisplayed, ['Subject']);
// Add remaining item.
MockInteractions.tap(
element.$$('.addOptions').querySelector('.add-button'));
flushAsynchronousOperations();
assert.deepEqual(element.changeTableNotDisplayed, []);
assert.deepEqual(element.changeTableItems, [
'Subject',
'Status',
'Owner',
'Project',
'Branch',
'Updated',
'Size'
]);
});
});
</script>

View File

@@ -20,6 +20,7 @@ limitations under the License.
<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-http-password/gr-http-password.html">
<link rel="import" href="../gr-change-table-editor/gr-change-table-editor.html">
<link rel="import" href="../gr-menu-editor/gr-menu-editor.html">
<link rel="import" href="../gr-ssh-editor/gr-ssh-editor.html">
<link rel="import" href="../gr-watched-projects-editor/gr-watched-projects-editor.html">
@@ -295,6 +296,19 @@ limitations under the License.
on-tap="_handleSaveMenu"
disabled="[[!_menuChanged]]">Save Changes</gr-button>
</fieldset>
<h2 class$="[[_computeHeaderClass(_changeTableChanged)]]">
Change Table Columns
</h2>
<fieldset id="changeTableColumns">
<gr-change-table-editor
change-table-items="{{_localChangeTableColumns}}"
change-table-not-displayed="{{_changeTableColumnsNotDisplayed}}">
</gr-change-table-editor>
<gr-button
id="saveChangeTable"
on-tap="_handleSaveChangeTable"
disabled="[[!_changeTableChanged]]">Save Changes</gr-button>
</fieldset>
<h2
id="Notifications"
class$="[[_computeHeaderClass(_watchedProjectsChanged)]]">

View File

@@ -39,10 +39,15 @@
_accountInfoMutable: Boolean,
_accountInfoChanged: Boolean,
_diffPrefs: Object,
_changeTableColumnsNotDisplayed: Array,
_localPrefs: {
type: Object,
value: function() { return {}; },
},
_localChangeTableColumns: {
type: Array,
value: function() { return []; },
},
_localMenu: {
type: Array,
value: function() { return []; },
@@ -51,6 +56,10 @@
type: Boolean,
value: true,
},
_changeTableChanged: {
type: Boolean,
value: false,
},
_prefsChanged: {
type: Boolean,
value: false,
@@ -89,10 +98,15 @@
_loadingPromise: Object,
},
behaviors: [
Gerrit.ChangeTableBehavior,
],
observers: [
'_handlePrefsChanged(_localPrefs.*)',
'_handleDiffPrefsChanged(_diffPrefs.*)',
'_handleMenuChanged(_localMenu.splices)',
'_handleChangeTableChanged(_localChangeTableColumns.splices)',
],
attached: function() {
@@ -110,6 +124,7 @@
this.prefs = prefs;
this._copyPrefs('_localPrefs', 'prefs');
this._cloneMenu();
this._cloneChangeTableColumns();
}.bind(this)));
promises.push(this.$.restAPI.getDiffPreferences().then(function(prefs) {
@@ -179,6 +194,30 @@
this._localMenu = menu;
},
_cloneChangeTableColumns: function() {
var columns = this.prefs.change_table;
if (columns.length === 0) {
columns = this.CHANGE_TABLE_COLUMNS;
this._changeTableColumnsNotDisplayed = [];
} else {
this._changeTableColumnsNotDisplayed = this.getComplementColumns(
this.prefs.change_table);
}
this._localChangeTableColumns = columns;
},
_formatChangeTableColumns: function(changeTableArray) {
return changeTableArray.map(function(item) {
return {column: item};
});
},
_handleChangeTableChanged: function() {
if (this._isLoading()) { return; }
this._changeTableChanged = true;
},
_handlePrefsChanged: function(prefs) {
if (this._isLoading()) { return; }
this._prefsChanged = true;
@@ -224,6 +263,14 @@
this.$.syntaxHighlighting.checked);
},
_handleSaveChangeTable: function() {
this.set('prefs.change_table', this._localChangeTableColumns);
this._cloneChangeTableColumns();
return this.$.restAPI.savePreferences(this.prefs).then(function() {
this._changeTableChanged = false;
}.bind(this));
},
_handleSaveDiffPreferences: function() {
return this.$.restAPI.saveDiffPreferences(this._diffPrefs)
.then(function() {

View File

@@ -88,6 +88,7 @@ limitations under the License.
{url: '/first/url', name: 'first name', target: '_blank'},
{url: '/second/url', name: 'second name', target: '_blank'},
],
change_table: [],
};
diffPreferences = {
context: 10,