diff --git a/_includes/recline-deps.html b/_includes/recline-deps.html
index 7cd8f7f4..4174fbfb 100644
--- a/_includes/recline-deps.html
+++ b/_includes/recline-deps.html
@@ -69,6 +69,7 @@
+
diff --git a/css/multiview.css b/css/multiview.css
index be894449..6837e7c2 100644
--- a/css/multiview.css
+++ b/css/multiview.css
@@ -110,6 +110,18 @@
width: 175px;
}
+.recline-filter-editor input {
+ margin-top: 0.5em;
+}
+
+.recline-filter-editor .add-filter {
+ margin-top: 1em;
+ margin-bottom: 2em;
+}
+
+.recline-filter-editor .update-filter {
+ margin-top: 1em;
+}
/**********************************************************
* Fields Widget
diff --git a/src/widget.valuefilter.js b/src/widget.valuefilter.js
new file mode 100644
index 00000000..60c08e65
--- /dev/null
+++ b/src/widget.valuefilter.js
@@ -0,0 +1,115 @@
+/*jshint multistr:true */
+
+this.recline = this.recline || {};
+this.recline.View = this.recline.View || {};
+
+(function($, my) {
+
+my.ValueFilter = Backbone.View.extend({
+ className: 'recline-filter-editor well',
+ template: ' \
+
\
+
Filters
\
+ \
+ \
+ \
+ \
+ ',
+ filterTemplates: {
+ term: ' \
+ \
+ '
+ },
+ events: {
+ 'click .js-remove-filter': 'onRemoveFilter',
+ 'click .js-add-filter': 'onAddFilterShow',
+ 'submit form.js-edit': 'onTermFiltersUpdate',
+ 'submit form.js-add': 'onAddFilter'
+ },
+ initialize: function() {
+ this.el = $(this.el);
+ _.bindAll(this, 'render');
+ this.model.fields.bind('all', this.render);
+ this.model.queryState.bind('change', this.render);
+ this.model.queryState.bind('change:filters:new-blank', this.render);
+ this.render();
+ },
+ render: function() {
+ var self = this;
+ var tmplData = $.extend(true, {}, this.model.queryState.toJSON());
+ // we will use idx in list as the id ...
+ tmplData.filters = _.map(tmplData.filters, function(filter, idx) {
+ filter.id = idx;
+ return filter;
+ });
+ tmplData.fields = this.model.fields.toJSON();
+ tmplData.filterRender = function() {
+ return Mustache.render(self.filterTemplates.term, this);
+ };
+ var out = Mustache.render(this.template, tmplData);
+ this.el.html(out);
+ },
+ updateFilter: function(input) {
+ var self = this;
+ var filters = self.model.queryState.get('filters');
+ var $input = $(input);
+ var filterIndex = parseInt($input.attr('data-filter-id'), 10);
+ var value = $input.val();
+ filters[filterIndex].term = value;
+ },
+ onAddFilterShow: function(e) {
+ e.preventDefault();
+ var $target = $(e.target);
+ $target.hide();
+ this.el.find('form.js-add').show();
+ },
+ onAddFilter: function(e) {
+ e.preventDefault();
+ var $target = $(e.target);
+ $target.hide();
+ var field = $target.find('select.fields').val();
+ this.model.queryState.addFilter({type: 'term', field: field});
+ },
+ onRemoveFilter: function(e) {
+ e.preventDefault();
+ var $target = $(e.target);
+ var filterId = $target.attr('data-filter-id');
+ this.model.queryState.removeFilter(filterId);
+ },
+ onTermFiltersUpdate: function(e) {
+ var self = this;
+ e.preventDefault();
+ var filters = self.model.queryState.get('filters');
+ var $form = $(e.target);
+ _.each($form.find('input'), function(input) {
+ self.updateFilter(input);
+ });
+ self.model.queryState.set({filters: filters, from: 0});
+ self.model.queryState.trigger('change');
+ }
+});
+
+})(jQuery, recline.View);
diff --git a/test/index.html b/test/index.html
index cfa1151e..31b8bb08 100644
--- a/test/index.html
+++ b/test/index.html
@@ -71,6 +71,7 @@
+
diff --git a/test/widget.valuefilter.test.js b/test/widget.valuefilter.test.js
new file mode 100644
index 00000000..6b8a86a1
--- /dev/null
+++ b/test/widget.valuefilter.test.js
@@ -0,0 +1,67 @@
+module("Widget - Value Filter");
+
+test('basics', function () {
+ var dataset = Fixture.getDataset();
+ var view = new recline.View.FilterEditor({
+ model: dataset
+ });
+ $('.fixtures').append(view.el);
+ assertPresent('.js-add-filter', view.elSidebar);
+ var $addForm = view.el.find('form.js-add');
+ ok(!$addForm.is(":visible"));
+ view.el.find('.js-add-filter').click();
+ ok(!view.el.find('.js-add-filter').is(":visible"));
+ ok($addForm.is(":visible"));
+
+ // submit the form
+ $addForm.find('select.fields').val('country');
+ $addForm.submit();
+
+ // now check we have new filter
+ ok(!$addForm.is(":visible"));
+ $editForm = view.el.find('form.js-edit');
+ equal($editForm.find('.filter-term').length, 1);
+ equal(dataset.queryState.attributes.filters[0].field, 'country');
+
+ // now set filter value and apply
+ $editForm.find('input').val('UK');
+ $editForm.submit();
+ equal(dataset.queryState.attributes.filters[0].term, 'UK');
+ equal(dataset.records.length, 3);
+
+ // now remove filter
+ $editForm = view.el.find('form.js-edit');
+ $editForm.find('.js-remove-filter').last().click();
+ $editForm = view.el.find('form.js-edit');
+ equal($editForm.find('.filter').length, 0);
+ equal(dataset.records.length, 6);
+
+ view.remove();
+});
+
+test('add 2 filters', function () {
+ var dataset = Fixture.getDataset();
+ var view = new recline.View.FilterEditor({
+ model: dataset
+ });
+ $('.fixtures').append(view.el);
+
+ // add 2 term filters
+ var $addForm = view.el.find('form.js-add');
+ view.el.find('.js-add-filter').click();
+ $addForm.find('select.fields').val('country');
+ $addForm.submit();
+
+ $addForm = view.el.find('form.js-add');
+ view.el.find('.js-add-filter').click();
+ $addForm.find('select.fields').val('id');
+ $addForm.submit();
+
+ var fields = [];
+ view.el.find('form.js-edit .filter-term input').each(function(idx, item) {
+ fields.push($(item).attr('data-filter-field'));
+ });
+ deepEqual(fields, ['country', 'id']);
+
+ view.remove();
+});