Add filter to projects page
Change-Id: I0d5167ee8cd1391b56dbb4e140d17f17f73f208a
This commit is contained in:
@@ -16,6 +16,7 @@ limitations under the License.
|
|||||||
|
|
||||||
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
|
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
|
||||||
<link rel="import" href="../../../behaviors/gr-url-encoding-behavior.html">
|
<link rel="import" href="../../../behaviors/gr-url-encoding-behavior.html">
|
||||||
|
<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
|
||||||
<link rel="import" href="../../../bower_components/polymer/polymer.html">
|
<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="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
|
||||||
|
|
||||||
@@ -35,6 +36,13 @@ limitations under the License.
|
|||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
#filterContainer {
|
||||||
|
margin: 1em;
|
||||||
|
}
|
||||||
|
#projectFilter {
|
||||||
|
font-size: 1em;
|
||||||
|
max-width: 25em;
|
||||||
|
}
|
||||||
td {
|
td {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
padding: .3em .5em;
|
padding: .3em .5em;
|
||||||
@@ -71,6 +79,14 @@ limitations under the License.
|
|||||||
padding: 1em var(--default-horizontal-margin);
|
padding: 1em var(--default-horizontal-margin);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
<div id="filterContainer">
|
||||||
|
<label>Filter:</label>
|
||||||
|
<input is="iron-input"
|
||||||
|
type="text"
|
||||||
|
id="projectFilter"
|
||||||
|
bind-value="{{_filter}}"
|
||||||
|
on-input="_onValueChange">
|
||||||
|
</div>
|
||||||
<table id="projectList">
|
<table id="projectList">
|
||||||
<tr class="headerRow">
|
<tr class="headerRow">
|
||||||
<th class="name topHeader">Project Name</th>
|
<th class="name topHeader">Project Name</th>
|
||||||
@@ -99,10 +115,10 @@ limitations under the License.
|
|||||||
</table>
|
</table>
|
||||||
<nav>
|
<nav>
|
||||||
<a id="prevArrow"
|
<a id="prevArrow"
|
||||||
href$="[[_computeNavLink(_offset, -1, _projectsPerPage)]]"
|
href$="[[_computeNavLink(_offset, -1, _projectsPerPage, _filter)]]"
|
||||||
hidden$="[[_hidePrevArrow(_offset)]]" hidden>← Prev</a>
|
hidden$="[[_hidePrevArrow(_offset)]]" hidden>← Prev</a>
|
||||||
<a id="nextArrow"
|
<a id="nextArrow"
|
||||||
href$="[[_computeNavLink(_offset, 1, _projectsPerPage)]]"
|
href$="[[_computeNavLink(_offset, 1, _projectsPerPage, _filter)]]"
|
||||||
hidden$="[[_hideNextArrow(_loading, _projects)]]" hidden>
|
hidden$="[[_hideNextArrow(_loading, _projects)]]" hidden>
|
||||||
Next →</a>
|
Next →</a>
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
(function() {
|
(function() {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
const REQUEST_DEBOUNCE_INTERVAL_MS = 200;
|
||||||
|
|
||||||
Polymer({
|
Polymer({
|
||||||
is: 'gr-admin-project-list',
|
is: 'gr-admin-project-list',
|
||||||
|
|
||||||
@@ -51,6 +53,7 @@
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
value: true,
|
value: true,
|
||||||
},
|
},
|
||||||
|
_filter: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
behaviors: [
|
behaviors: [
|
||||||
@@ -63,16 +66,35 @@
|
|||||||
'previous-page': '_handlePreviousPage',
|
'previous-page': '_handlePreviousPage',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_onValueChange(e) {
|
||||||
|
this.debounce('reload', () => {
|
||||||
|
if (e.target.value) {
|
||||||
|
return page.show('/admin/projects/q/filter:' +
|
||||||
|
this.encodeURL(e.target.value, false));
|
||||||
|
}
|
||||||
|
page.show('/admin/projects');
|
||||||
|
}, REQUEST_DEBOUNCE_INTERVAL_MS);
|
||||||
|
},
|
||||||
|
|
||||||
_paramsChanged(value) {
|
_paramsChanged(value) {
|
||||||
this._loading = true;
|
this._loading = true;
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
this._filter = value.filter || null;
|
||||||
|
}
|
||||||
|
|
||||||
if (value && value.offset) {
|
if (value && value.offset) {
|
||||||
this._offset = value.offset;
|
this._offset = value.offset;
|
||||||
} else {
|
} else {
|
||||||
this._offset = 0;
|
this._offset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.$.restAPI.getProjects(this._projectsPerPage, this._offset)
|
return this._getProjects(this._filter, this._projectsPerPage,
|
||||||
|
this._offset);
|
||||||
|
},
|
||||||
|
|
||||||
|
_getProjects(filter, projectsPerPage, offset) {
|
||||||
|
return this.$.restAPI.getProjects(filter, projectsPerPage, offset)
|
||||||
.then(projects => {
|
.then(projects => {
|
||||||
if (!projects) {
|
if (!projects) {
|
||||||
this._projects = [];
|
this._projects = [];
|
||||||
@@ -106,11 +128,14 @@
|
|||||||
return webLinks.length ? webLinks : null;
|
return webLinks.length ? webLinks : null;
|
||||||
},
|
},
|
||||||
|
|
||||||
_computeNavLink(offset, direction, projectsPerPage) {
|
_computeNavLink(offset, direction, projectsPerPage, filter) {
|
||||||
// Offset could be a string when passed from the router.
|
// Offset could be a string when passed from the router.
|
||||||
offset = +(offset || 0);
|
offset = +(offset || 0);
|
||||||
const newOffset = Math.max(0, offset + (projectsPerPage * direction));
|
const newOffset = Math.max(0, offset + (projectsPerPage * direction));
|
||||||
let href = this.getBaseUrl() + '/admin/projects';
|
let href = this.getBaseUrl() + '/admin/projects';
|
||||||
|
if (filter) {
|
||||||
|
href += '/q/filter:' + filter;
|
||||||
|
}
|
||||||
if (newOffset > 0) {
|
if (newOffset > 0) {
|
||||||
href += ',' + newOffset;
|
href += ',' + newOffset;
|
||||||
}
|
}
|
||||||
@@ -132,17 +157,5 @@
|
|||||||
}
|
}
|
||||||
return loading || lastPage || !projects || !projects.length;
|
return loading || lastPage || !projects || !projects.length;
|
||||||
},
|
},
|
||||||
|
|
||||||
_handleNextPage() {
|
|
||||||
if (this.$.nextArrow.hidden) { return; }
|
|
||||||
page.show(this._computeNavLink(
|
|
||||||
this._offset, 1, this._projectsPerPage));
|
|
||||||
},
|
|
||||||
|
|
||||||
_handlePreviousPage() {
|
|
||||||
if (this.$.prevArrow.hidden) { return; }
|
|
||||||
page.show(this._computeNavLink(
|
|
||||||
this._offset, -1, this._projectsPerPage));
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -52,8 +52,18 @@ limitations under the License.
|
|||||||
suite('gr-admin-project-list tests', () => {
|
suite('gr-admin-project-list tests', () => {
|
||||||
let element;
|
let element;
|
||||||
let projects;
|
let projects;
|
||||||
|
let sandbox;
|
||||||
let value;
|
let value;
|
||||||
|
|
||||||
|
setup(() => {
|
||||||
|
sandbox = sinon.sandbox.create();
|
||||||
|
element = fixture('basic');
|
||||||
|
});
|
||||||
|
|
||||||
|
teardown(() => {
|
||||||
|
sandbox.restore();
|
||||||
|
});
|
||||||
|
|
||||||
suite('list with projects', () => {
|
suite('list with projects', () => {
|
||||||
setup(done => {
|
setup(done => {
|
||||||
projects = _.times(26, projectGenerator);
|
projects = _.times(26, projectGenerator);
|
||||||
@@ -64,7 +74,6 @@ limitations under the License.
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
element = fixture('basic');
|
|
||||||
element._paramsChanged(value).then(() => { flush(done); });
|
element._paramsChanged(value).then(() => { flush(done); });
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -115,7 +124,6 @@ limitations under the License.
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
element = fixture('basic');
|
|
||||||
element._paramsChanged(value).then(() => { flush(done); });
|
element._paramsChanged(value).then(() => { flush(done); });
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -135,5 +143,55 @@ limitations under the License.
|
|||||||
assert.equal(element._shownProjects.length, 25);
|
assert.equal(element._shownProjects.length, 25);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
suite('filter', () => {
|
||||||
|
test('_onValueChange', done => {
|
||||||
|
sandbox.stub(page, 'show', url => {
|
||||||
|
assert.equal(url, '/admin/projects/q/filter:test');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
const e = {target: {value: 'test'}};
|
||||||
|
element._onValueChange(e);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('_paramsChanged', done => {
|
||||||
|
sandbox.stub(element.$.restAPI, 'getProjects', () => {
|
||||||
|
return Promise.resolve(projects);
|
||||||
|
});
|
||||||
|
const value = {
|
||||||
|
filter: 'test',
|
||||||
|
offset: 25,
|
||||||
|
};
|
||||||
|
element._paramsChanged(value).then(() => {
|
||||||
|
assert.isTrue(element.$.restAPI.getProjects.lastCall
|
||||||
|
.calledWithExactly('test', 25, 25));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('_computeNavLink', () => {
|
||||||
|
const offset = 25;
|
||||||
|
const projectsPerPage = 25;
|
||||||
|
const filter = 'test';
|
||||||
|
|
||||||
|
sandbox.stub(element, 'getBaseUrl', () => '');
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
element._computeNavLink(offset, 1, projectsPerPage, filter),
|
||||||
|
'/admin/projects/q/filter:test,50');
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
element._computeNavLink(offset, -1, projectsPerPage, filter),
|
||||||
|
'/admin/projects/q/filter:test');
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
element._computeNavLink(offset, 1, projectsPerPage, null),
|
||||||
|
'/admin/projects,50');
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
element._computeNavLink(offset, -1, projectsPerPage, null),
|
||||||
|
'/admin/projects');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -113,6 +113,22 @@
|
|||||||
app.params = {
|
app.params = {
|
||||||
view: 'gr-admin-project-list',
|
view: 'gr-admin-project-list',
|
||||||
offset: data.params[1] || 0,
|
offset: data.params[1] || 0,
|
||||||
|
filter: null,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
page('/admin/projects/q/filter::filter,:offset', loadUser, data => {
|
||||||
|
app.params = {
|
||||||
|
view: 'gr-admin-project-list',
|
||||||
|
offset: data.params.offset,
|
||||||
|
filter: data.params.filter,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
page('/admin/projects/q/filter::filter', loadUser, data => {
|
||||||
|
app.params = {
|
||||||
|
view: 'gr-admin-project-list',
|
||||||
|
filter: data.params.filter || null,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -559,10 +559,12 @@
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
getProjects(projectsPerPage, opt_offset) {
|
getProjects(filter, projectsPerPage, opt_offset) {
|
||||||
const offset = opt_offset || 0;
|
const offset = opt_offset || 0;
|
||||||
|
filter = filter ? '&m=' + filter : '';
|
||||||
|
|
||||||
return this._fetchSharedCacheURL(
|
return this._fetchSharedCacheURL(
|
||||||
`/projects/?d&n=${projectsPerPage + 1}&S=${offset}`
|
`/projects/?d&n=${projectsPerPage + 1}&S=${offset}${filter}`
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -643,5 +643,20 @@ limitations under the License.
|
|||||||
'POST', '/changes/foo/revisions/bar/comments/01234/delete',
|
'POST', '/changes/foo/revisions/bar/comments/01234/delete',
|
||||||
{reason: 'removal reason'}));
|
{reason: 'removal reason'}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('getProjects', () => {
|
||||||
|
sandbox.stub(element, '_fetchSharedCacheURL');
|
||||||
|
element.getProjects('test', 25);
|
||||||
|
assert.isTrue(element._fetchSharedCacheURL.lastCall
|
||||||
|
.calledWithExactly('/projects/?d&n=26&S=0&m=test'));
|
||||||
|
|
||||||
|
element.getProjects(null, 25);
|
||||||
|
assert.isTrue(element._fetchSharedCacheURL.lastCall
|
||||||
|
.calledWithExactly('/projects/?d&n=26&S=0'));
|
||||||
|
|
||||||
|
element.getProjects('test', 25, 25);
|
||||||
|
assert.isTrue(element._fetchSharedCacheURL.lastCall
|
||||||
|
.calledWithExactly('/projects/?d&n=26&S=25&m=test'));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user