diff options
author | Garren Smith <garren.smith@gmail.com> | 2013-09-06 14:49:36 +0200 |
---|---|---|
committer | Garren Smith <garren.smith@gmail.com> | 2013-09-10 16:17:16 +0200 |
commit | d081ae59821e9abe9f37e1c82a94aafd95a2a339 (patch) | |
tree | 141059f8f721e7f9267896ec2554c7ffd1679bc4 | |
parent | 4ac5314ae15d6105113efc03678a85de46141329 (diff) | |
download | couchdb-d081ae59821e9abe9f37e1c82a94aafd95a2a339.tar.gz |
Fauxton: Index pagination basics working and tests
-rw-r--r-- | src/fauxton/app/modules/databases/views.js | 10 | ||||
-rw-r--r-- | src/fauxton/app/modules/documents/resources.js | 64 | ||||
-rw-r--r-- | src/fauxton/app/modules/documents/tests/resourcesSpec.js | 84 | ||||
-rw-r--r-- | src/fauxton/app/modules/documents/views.js | 36 | ||||
-rw-r--r-- | src/fauxton/app/modules/fauxton/base.js | 23 | ||||
-rw-r--r-- | src/fauxton/app/modules/fauxton/paginate.js | 93 | ||||
-rw-r--r-- | src/fauxton/app/templates/documents/all_docs_list.html | 15 | ||||
-rw-r--r-- | src/fauxton/app/templates/fauxton/index_pagination.html | 24 | ||||
-rw-r--r-- | src/fauxton/test/core/paginateSpec.js | 100 |
9 files changed, 403 insertions, 46 deletions
diff --git a/src/fauxton/app/modules/databases/views.js b/src/fauxton/app/modules/databases/views.js index c177264a1..ef7584127 100644 --- a/src/fauxton/app/modules/databases/views.js +++ b/src/fauxton/app/modules/databases/views.js @@ -13,11 +13,11 @@ define([ "app", - "modules/fauxton/base", + "modules/fauxton/paginate", "api" ], -function(app, Fauxton, FauxtonAPI) { +function(app, Paginate, FauxtonAPI) { var Views = {}; Views.Item = FauxtonAPI.View.extend({ @@ -32,8 +32,8 @@ function(app, Fauxton, FauxtonAPI) { }); Views.List = FauxtonAPI.View.extend({ - dbLimit: 10, - perPage: 10, + dbLimit: 20, + perPage: 20, template: "templates/databases/list", events: { "click button.all": "selectAll", @@ -83,7 +83,7 @@ function(app, Fauxton, FauxtonAPI) { })); }, this); - this.insertView("#database-pagination", new Fauxton.Pagination({ + this.insertView("#database-pagination", new Paginate.Pagination({ page: this.page, perPage: this.perPage, total: this.collection.length, diff --git a/src/fauxton/app/modules/documents/resources.js b/src/fauxton/app/modules/documents/resources.js index 0a80ad83a..a9e9836d6 100644 --- a/src/fauxton/app/modules/documents/resources.js +++ b/src/fauxton/app/modules/documents/resources.js @@ -255,14 +255,41 @@ function(app, FauxtonAPI) { this.params = options.params; }, - url: function() { + url: function(context) { var query = ""; if (this.params) { query = "?" + $.param(this.params); } + + if (context === 'app') { + return 'database/' + this.database.id + "/_all_docs" + query; + } return app.host + "/" + this.database.id + "/_all_docs" + query; }, + urlNextPage: function (num, lastId) { + if (!lastId) { + lastId = this.last().id; + } + + this.params.startkey_docid = '"' + lastId + '"'; + this.params.startkey = '"' + lastId + '"'; + this.params.limit = num; + return this.url('app'); + }, + + urlPreviousPage: function (num, firstId) { + this.params.limit = num; + if (firstId) { + this.params.startkey_docid = '"' + firstId + '"'; + this.params.startkey = '"' + firstId + '"'; + } else { + delete this.params.startkey; + delete this.params.startkey_docid; + } + return this.url('app'); + }, + totalRows: function() { return this.viewMeta.total_rows || "unknown"; }, @@ -301,15 +328,44 @@ function(app, FauxtonAPI) { this.design = options.design.replace('_design/',''); }, - url: function() { + url: function(context) { var query = ""; if (this.params) { query = "?" + $.param(this.params); } - var url = [app.host, this.database.id, "_design", this.design, this.idxType, this.view]; + + var startOfUrl = app.host; + if (context === 'app') { + startOfUrl = 'database'; + } + + var url = [startOfUrl, this.database.id, "_design", this.design, this.idxType, this.view]; return url.join("/") + query; }, + urlNextPage: function (num, lastId) { + if (!lastId) { + lastId = this.last().id; + } + + this.params.startkey_docid = '"' + lastId + '"'; + this.params.startkey = '"' + lastId + '"'; + this.params.limit = num; + return this.url('app'); + }, + + urlPreviousPage: function (num, firstId) { + this.params.limit = num; + if (firstId) { + this.params.startkey_docid = '"' + firstId + '"'; + this.params.startkey = '"' + firstId + '"'; + } else { + delete this.params.startkey; + delete this.params.startkey_docid; + } + return this.url('app'); + }, + totalRows: function() { return this.viewMeta.total_rows || "unknown"; }, @@ -324,7 +380,7 @@ function(app, FauxtonAPI) { this.viewMeta = { total_rows: resp.total_rows, - offest: resp.offest, + offset: resp.offset, update_seq: resp.update_seq }; return _.map(resp.rows, function(row) { diff --git a/src/fauxton/app/modules/documents/tests/resourcesSpec.js b/src/fauxton/app/modules/documents/tests/resourcesSpec.js new file mode 100644 index 000000000..35bbdb367 --- /dev/null +++ b/src/fauxton/app/modules/documents/tests/resourcesSpec.js @@ -0,0 +1,84 @@ +// 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. +define([ + 'modules/documents/resources', + 'testUtils' +], function (Models, testUtils) { + var assert = testUtils.assert; + + describe('IndexCollection', function () { + var collection; + beforeEach(function () { + collection = new Models.IndexCollection([{ + id:'myId1', + doc: 'num1' + }, + { + id:'myId2', + doc: 'num2' + }], { + database: {id: 'databaseId'}, + design: '_design/myDoc' + }); + + }); + + it('Should return urlNext', function () { + var url = collection.urlNextPage(20); + + assert.equal(url, 'database/databaseId/_design/myDoc/_view/?limit=20&reduce=false&startkey_docid=%22myId2%22&startkey=%22myId2%22'); + + }); + + it('Should return urlPrevious', function () { + var url = collection.urlPreviousPage(20, 'myId1'); + + assert.equal(url, 'database/databaseId/_design/myDoc/_view/?limit=20&reduce=false&startkey_docid=%22myId1%22&startkey=%22myId1%22'); + + }); + + }); + + describe('AllDocs', function () { + var collection; + beforeEach(function () { + collection = new Models.AllDocs([{ + _id:'myId1', + doc: 'num1' + }, + { + _id:'myId2', + doc: 'num2' + }], { + database: {id: 'databaseId'}, + params: {limit: 20} + }); + + }); + + it('Should return urlNext', function () { + var url = collection.urlNextPage(20); + + assert.equal(url, 'database/databaseId/_all_docs?limit=20&startkey_docid=%22myId2%22&startkey=%22myId2%22'); + + }); + + it('Should return urlPrevious', function () { + var url = collection.urlPreviousPage(20, 'myId1'); + assert.equal(url, 'database/databaseId/_all_docs?limit=20&startkey_docid=%22myId1%22&startkey=%22myId1%22'); + }); + + + }); + +}); + diff --git a/src/fauxton/app/modules/documents/views.js b/src/fauxton/app/modules/documents/views.js index 83e1e8fd3..c345c9cb7 100644 --- a/src/fauxton/app/modules/documents/views.js +++ b/src/fauxton/app/modules/documents/views.js @@ -14,6 +14,7 @@ define([ "app", "api", + "modules/fauxton/paginate", "modules/documents/resources", "modules/pouchdb/base", @@ -29,7 +30,7 @@ define([ ], -function(app, FauxtonAPI, Documents, pouchdb, Codemirror, JSHint, resizeColumns) { +function(app, FauxtonAPI, Paginate, Documents, pouchdb, Codemirror, JSHint, resizeColumns) { var Views = {}; Views.Tabs = FauxtonAPI.View.extend({ @@ -435,6 +436,7 @@ function(app, FauxtonAPI, Documents, pouchdb, Codemirror, JSHint, resizeColumns) this.ddocID = options.ddocInfo.id; } this.newView = options.newView || false; + this.addPagination(); }, establish: function() { @@ -510,7 +512,39 @@ function(app, FauxtonAPI, Documents, pouchdb, Codemirror, JSHint, resizeColumns) }, this); }, + addPagination: function () { + var collection = this.collection; + + this.pagination = new Paginate.IndexPagination({ + collection: this.collection, + scrollToSelector: '#dashboard-lower-content', + previousUrlfn: function () { + return collection.urlPreviousPage(20, this.previousIds.pop()); + }, + canShowPreviousfn: function () { + if (collection.viewMeta.offset === 0) { + return false; + } + + return true; + }, + canShowNextfn: function () { + + if ((collection.viewMeta.offset + 1) === collection.viewMeta.total_rows) { + return false; + } + + return true; + }, + + nextUrlfn: function () { + return collection.urlNextPage(20); + } + }); + }, + beforeRender: function() { + this.insertView('#documents-pagination', this.pagination); this.collection.each(function(doc) { this.rows[doc.id] = this.insertView("table.all-docs tbody", new this.nestedView({ model: doc diff --git a/src/fauxton/app/modules/fauxton/base.js b/src/fauxton/app/modules/fauxton/base.js index 8fa40fa2f..a4b3a5e82 100644 --- a/src/fauxton/app/modules/fauxton/base.js +++ b/src/fauxton/app/modules/fauxton/base.js @@ -260,27 +260,6 @@ function(app, Backbone, resizeColumns) { } }); - Fauxton.Pagination = Backbone.View.extend({ - template: "templates/fauxton/pagination", - - initialize: function(options) { - this.page = parseInt(options.page, 10); - this.perPage = options.perPage; - this.total = options.total; - this.totalPages = Math.ceil(this.total / this.perPage); - this.urlFun = options.urlFun; - }, - - serialize: function() { - return { - page: this.page, - perPage: this.perPage, - total: this.total, - totalPages: this.totalPages, - urlFun: this.urlFun - }; - } - }); - + return Fauxton; }); diff --git a/src/fauxton/app/modules/fauxton/paginate.js b/src/fauxton/app/modules/fauxton/paginate.js new file mode 100644 index 000000000..a32bbbe62 --- /dev/null +++ b/src/fauxton/app/modules/fauxton/paginate.js @@ -0,0 +1,93 @@ +// 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. + +define([ + "app", + // Libs + "api" +], + +function(app, FauxtonAPI) { + var Paginate = app.module(); + + Paginate.Pagination = FauxtonAPI.View.extend({ + template: "templates/fauxton/pagination", + + initialize: function(options) { + this.page = parseInt(options.page, 10); + this.perPage = options.perPage; + this.total = options.total; + this.totalPages = Math.ceil(this.total / this.perPage); + this.urlFun = options.urlFun; + }, + + serialize: function() { + return { + page: this.page, + perPage: this.perPage, + total: this.total, + totalPages: this.totalPages, + urlFun: this.urlFun + }; + } + }); + + Paginate.IndexPagination = FauxtonAPI.View.extend({ + template: "templates/fauxton/index_pagination", + events: { + "click a": 'scrollTo', + "click a#next": 'nextClicked', + "click a#previous": 'previousClicked' + }, + + currentDirection: 'next', + previousIds: [], + + scrollTo: function () { + if (!this.scrollToSelector) { return; } + $(this.scrollToSelector).animate({ scrollTop: 0 }, 'slow'); + }, + + initialize: function (options) { + this.previousUrlfn = options.previousUrlfn; + this.nextUrlfn = options.nextUrlfn; + this.canShowPreviousfn = options.canShowPreviousfn; + this.canShowNextfn = options.canShowNextfn; + this.scrollToSelector = options.scrollToSelector; + _.bindAll(this); + }, + + previousClicked: function (event) { + event.preventDefault(); + this.currentDirection = 'previous'; + FauxtonAPI.navigate(this.previousUrlfn()); + }, + + nextClicked: function (event) { + event.preventDefault(); + this.currentDirection = 'next'; + this.previousIds.push(this.collection.first().id); + FauxtonAPI.navigate(this.nextUrlfn()); + }, + + serialize: function () { + return { + canShowNextfn: this.canShowNextfn, + canShowPreviousfn: this.canShowPreviousfn, + }; + } + + }); + + return Paginate; +}); + diff --git a/src/fauxton/app/templates/documents/all_docs_list.html b/src/fauxton/app/templates/documents/all_docs_list.html index fd1a5b0b2..f60257551 100644 --- a/src/fauxton/app/templates/documents/all_docs_list.html +++ b/src/fauxton/app/templates/documents/all_docs_list.html @@ -41,18 +41,5 @@ the License. <table class="all-docs table table-striped table-condensed"> <tbody></tbody> </table> - <!-- - <div class="pagination pagination-centered"> - <ul> - <li class="disabled"><a href="#">«</a></li> - <li class="active"><a href="#">1</a></li> - <li><a href="#">2</a></li> - <li><a href="#">3</a></li> - <li><a href="#">4</a></li> - <li><a href="#">5</a></li> - <li><a href="#">»</a></li> - </ul> - </div> - --> - + <div id="documents-pagination"></div> </div> diff --git a/src/fauxton/app/templates/fauxton/index_pagination.html b/src/fauxton/app/templates/fauxton/index_pagination.html new file mode 100644 index 000000000..f445377d8 --- /dev/null +++ b/src/fauxton/app/templates/fauxton/index_pagination.html @@ -0,0 +1,24 @@ +<!-- +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. +--> + +<div class="pagination pagination-centered"> + <ul> + <li <% if (!canShowPreviousfn()) {%> class="disabled" <% } %>> + <a id="previous" href="#"> Previous </a> + </li> + <li <% if (!canShowNextfn()) {%> class="disabled" <% } %>> + <a id="next" href="#"> Next </a></li> + </ul> +</div> + diff --git a/src/fauxton/test/core/paginateSpec.js b/src/fauxton/test/core/paginateSpec.js new file mode 100644 index 000000000..6b7bfb6e7 --- /dev/null +++ b/src/fauxton/test/core/paginateSpec.js @@ -0,0 +1,100 @@ +// 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. +define([ + 'api', + 'modules/fauxton/paginate', + 'modules/documents/resources', + 'testUtils', + 'app' +], function (FauxtonAPI, Views, Models, testUtils, app) { + var assert = testUtils.assert, + ViewSandbox = testUtils.ViewSandbox; + + + describe('IndexPaginate', function () { + var viewSandbox, paginate, collection, navigateMock; + beforeEach(function () { + app.router = { + navigate: function () {} + }; + + collection = new Models.IndexCollection([{ + id:'myId1', + doc: 'num1' + }, + { + id:'myId2', + doc: 'num2' + }], { + database: {id: 'databaseId'}, + design: '_design/myDoc' + }); + + paginate = new Views.IndexPagination({ + collection: collection, + previousUrlfn: function () {}, + nextUrlfn: function () {}, + canShowPreviousfn: function () {}, + canShowNextfn: function () {} + }); + viewSandbox = new ViewSandbox(); + viewSandbox.renderView(paginate); + }); + + afterEach(function () { + viewSandbox.remove(); + }); + + describe('#next', function () { + + it('should set direction as next', function () { + paginate.$('a#next').click(); + + assert.equal(paginate.currentDirection, 'next'); + + }); + + it('Should navigate', function () { + var navigateMock = sinon.spy(FauxtonAPI, 'navigate'); + + paginate.$('a#next').click(); + + assert.ok(navigateMock.calledOnce); + FauxtonAPI.navigate.restore(); + }); + + }); + + + describe('#previous', function () { + + it('should set direction as next', function () { + paginate.$('a#previous').click(); + + assert.equal(paginate.currentDirection, 'previous'); + + }); + + it('Should navigate', function () { + var navigateMock = sinon.spy(FauxtonAPI, 'navigate'); + + paginate.$('a#previous').click(); + + assert.ok(navigateMock.calledOnce); + FauxtonAPI.navigate.restore(); + }); + + + }); + + }); +}); |