From 8c1a1359dbff65459ba0ba61719f178cc6373e67 Mon Sep 17 00:00:00 2001 From: kielni Date: Fri, 17 Jan 2014 13:07:41 -0800 Subject: [PATCH] modify pager widget to handle boundary conditions; now takes a Dataset instead of a Query so that it has access to the record count; add tests --- demos/search/demo.search.app.js | 2 +- src/view.multiview.js | 2 +- src/widget.pager.js | 27 ++++++--- test/index.html | 1 + test/widget.pager.test.js | 99 +++++++++++++++++++++++++++++++++ 5 files changed, 120 insertions(+), 11 deletions(-) create mode 100644 test/widget.pager.test.js diff --git a/demos/search/demo.search.app.js b/demos/search/demo.search.app.js index 49f5f9b9..9621ffbf 100644 --- a/demos/search/demo.search.app.js +++ b/demos/search/demo.search.app.js @@ -143,7 +143,7 @@ var SearchView = Backbone.View.extend({ this.el.find('.sidebar').append(view.el); var pager = new recline.View.Pager({ - model: this.model.queryState + model: this.model }); this.el.find('.pager-here').append(pager.el); diff --git a/src/view.multiview.js b/src/view.multiview.js index ccd35171..918f0e16 100644 --- a/src/view.multiview.js +++ b/src/view.multiview.js @@ -260,7 +260,7 @@ my.MultiView = Backbone.View.extend({ }, this); this.pager = new recline.View.Pager({ - model: this.model.queryState + model: this.model }); this.$el.find('.recline-results-info').after(this.pager.el); diff --git a/src/widget.pager.js b/src/widget.pager.js index b2b5d03d..05e79237 100644 --- a/src/widget.pager.js +++ b/src/widget.pager.js @@ -25,34 +25,43 @@ my.Pager = Backbone.View.extend({ initialize: function() { _.bindAll(this, 'render'); - this.listenTo(this.model, 'change', this.render); + this.listenTo(this.model.queryState, 'change', this.render); this.render(); }, onFormSubmit: function(e) { e.preventDefault(); var newFrom = parseInt(this.$el.find('input[name="from"]').val()); + newFrom = Math.min(this.model.recordCount, Math.max(newFrom, 1))-1; var newSize = parseInt(this.$el.find('input[name="to"]').val()) - newFrom; - newFrom = Math.max(newFrom, 0); - newSize = Math.max(newSize, 1); - this.model.set({size: newSize, from: newFrom}); + newSize = Math.min(Math.max(newSize, 1), this.model.recordCount); + this.model.queryState.set({size: newSize, from: newFrom}); }, onPaginationUpdate: function(e) { e.preventDefault(); var $el = $(e.target); var newFrom = 0; + var currFrom = this.model.queryState.get('from'); + var size = this.model.queryState.get('size'); + var updateQuery = false; if ($el.parent().hasClass('prev')) { - newFrom = this.model.get('from') - Math.max(0, this.model.get('size')); + newFrom = Math.max(currFrom - Math.max(0, size), 1)-1; + updateQuery = newFrom != currFrom; } else { - newFrom = this.model.get('from') + this.model.get('size'); + newFrom = Math.max(currFrom + size, 1); + updateQuery = (newFrom < this.model.recordCount); + } + if (updateQuery) { + this.model.queryState.set({from: newFrom}); } - newFrom = Math.max(newFrom, 0); - this.model.set({from: newFrom}); }, render: function() { var tmplData = this.model.toJSON(); - tmplData.to = this.model.get('from') + this.model.get('size'); + var from = parseInt(this.model.queryState.get('from')); + tmplData.from = from+1; + tmplData.to = Math.min(from+this.model.queryState.get('size'), this.model.recordCount); var templated = Mustache.render(this.template, tmplData); this.$el.html(templated); + return this; } }); diff --git a/test/index.html b/test/index.html index 1dc22ed8..87698280 100644 --- a/test/index.html +++ b/test/index.html @@ -71,6 +71,7 @@ +

Qunit Tests

diff --git a/test/widget.pager.test.js b/test/widget.pager.test.js new file mode 100644 index 00000000..4274fede --- /dev/null +++ b/test/widget.pager.test.js @@ -0,0 +1,99 @@ +module("Widget - Pager"); + +test('basics', function () { + var dataset = Fixture.getDataset(); + var size = dataset.recordCount/2 + 1; + dataset.queryState.set({ size : size }, { silent : true }); + var view = new recline.View.Pager({ + model: dataset + }); + $('.fixtures').append(view.el); + var fromSelector = 'input[name=from]'; + var toSelector = 'input[name=to]'; + + assertPresent('.pagination', view.elSidebar); + // next and prev present + assertPresent('.prev', view.elSidebar); + assertPresent('.next', view.elSidebar); + + // from and to inputs present + assertPresent(fromSelector, view.elSidebar); + assertPresent(toSelector, view.elSidebar); + + // click next: -> reload from+size - recordCount + var prevFromVal = parseInt($(fromSelector).val()); + var prevToVal = parseInt($(toSelector).val()); + view.$el.find('.next a').click(); + equal($(fromSelector).val(), prevFromVal+size); + // to = recordCount since size is more than half of record count + equal($(toSelector).val(), dataset.recordCount); + // UI is 1-based but model is zero-based + equal(dataset.queryState.get('from'), prevFromVal+size-1); + + // click prev -> 1-4, model from=0 + prevFromVal = parseInt($(fromSelector).val()); + prevToVal = parseInt($(toSelector).val()); + view.$el.find('.prev a').click(); + equal($(fromSelector).val(), prevFromVal-size); + equal($(toSelector).val(), prevFromVal-1); + // UI is 1-based but model is zero-based + equal(dataset.queryState.get('from'), prevFromVal-size-1); + + view.remove(); +}); + +test('bounds checking', function () { + var dataset = Fixture.getDataset(); + var size = dataset.recordCount/2 + 1; + dataset.queryState.set({ size : size }, { silent : true }); + var view = new recline.View.Pager({ + model: dataset + }); + $('.fixtures').append(view.el); + var querySpy = sinon.spy(dataset, 'query'); + var fromSelector = 'input[name=from]'; + var toSelector = 'input[name=to]'; + + // click prev on beginning: nothing happens + view.$el.find('.prev a').click(); + equal($(fromSelector).val(), 1); + equal($(toSelector).val(), size); + ok(!dataset.query.called); + + // enter size-1 in from: reloads size-1 - size + var fromVal = size-1; + var toVal = parseInt($(toSelector).val()); + $(fromSelector).val(fromVal).change(); + equal($(fromSelector).val(), fromVal); + equal($(toSelector).val(), toVal); + // UI is 1-based but model is zero-based + equal(dataset.queryState.get('from'), fromVal-1); + + // enter value past the end in from: reloads recordCount - recordCount + fromVal = dataset.recordCount + 10; + $(fromSelector).val(fromVal).change(); + equal($(fromSelector).val(), dataset.recordCount); + equal($(toSelector).val(), dataset.recordCount); + // UI is 1-based but model is zero-based + equal(dataset.queryState.get('from'), dataset.recordCount-1); + + // click next on end -> nothing happens + var queryCalls = querySpy.callCount; + fromVal = parseInt($(fromSelector).val()); + toVal = parseInt($(toSelector).val()); + view.$el.find('.next a').click(); + equal(querySpy.callCount, queryCalls); + equal($(fromSelector).val(), fromVal); + equal($(toSelector).val(), toVal); + + // reset from to 1 + // type value past the end in to: 1-recordCount + fromVal = 1; + toVal = dataset.recordCount + 10; + $(fromSelector).val(fromVal); + $(toSelector).val(toVal).change(); + equal($(fromSelector).val(), 1); + equal($(toSelector).val(), dataset.recordCount); + + view.remove(); +});