Skip to content

Commit

Permalink
add table field
Browse files Browse the repository at this point in the history
  • Loading branch information
ajb committed Feb 27, 2014
1 parent 5713039 commit 8d20b6f
Show file tree
Hide file tree
Showing 5 changed files with 301 additions and 4 deletions.
199 changes: 199 additions & 0 deletions app/models/formbuilder/response_field_table.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
module Formbuilder
class ResponseFieldTable < ResponseField

include ActionView::Helpers::TagHelper
include ActionView::Context

after_initialize -> {
@serialized = true
@field_type = 'table'
@search_type = 'table'
}

def columns_array
Array(self[:field_options]['columns']).map { |column| column['label'] }
end

def render_input(value, opts = {})
value ||= {}

str = """
<table class='border_all'>
<thead>
<tr>
"""

columns_array.each_with_index do |column, i|
str += """<th>#{column}</th>"""
end

str += """
</tr>
</thead>
<tbody>
"""

[self[:field_options]['minrows'].to_i, 1, number_of_saved_rows(value)].max.times do |i|
str += """
<tr>
"""

columns_array.each_with_index do |column, j|
str += """
<td>
<input type='text'
name='response_fields[#{self[:id]}][#{j}][]'
value='#{value[column].try(:[], i)}'
data-col='#{j}'
data-row='#{i}' />
</td>
"""
end

str += """
</tr>
"""
end

str += """
</tbody>
"""

if self[:field_options]['column_totals']
str += """
<tfoot>
"""

columns_array.each_with_index do |column, j|
str += """
<td><span></span></td>
"""
end

str += """
</tfoot>
"""
end

str += """
</table>
"""

str += """
<div class='margin_th margin_bh'>
<a class='uppercase add_another_row'><i class='icon-plus-sign'></i> Add another row</a>
</div>
"""

str.squish
end

def render_entry(value, opts = {})
value ||= {}

str = """
<table class='border_all'>
<thead>
<tr>
"""

columns_array.each_with_index do |column, i|
str += """<th>#{column}</th>"""
end

str += """
</tr>
</thead>
<tbody>
"""

number_of_saved_rows(value).times do |i|
str += """
<tr>
"""

columns_array.each_with_index do |column, j|
str += """
<td>#{value[column].try(:[], i)}</td>
"""
end

str += """
</tr>
"""
end

str += """
</tbody>
"""

if self[:field_options]['column_totals']
str += """
<tfoot>
"""

columns_array.each_with_index do |column, j|
str += """
<td><span>#{opts.try(:[], :entry).try(:get_responses).try(:[], "#{self.id}_sum_#{column}")}</span></td>
"""
end

str += """
</tfoot>
"""
end

str += """
</table>
"""

str.squish
end

def render_entry_text(value, opts = {})
(value || {}).map do |k, v|
"#{k}: " + Array(v).join(', ')
end.join("; ")
end

def sortable_value(value)
nil # see :normalize_response for override
end

def number_of_saved_rows(value)
value.try(:first).try(:[], 1).try(:length) || 0
end

def normalize_response(value, all_responses)
# Calculate sums and store in the hstore
columns_array.each do |column|
all_responses["#{self.id}_sum_#{column}"] = Array(value[column]).map do |x|
x.gsub(/\$?,?/, '').to_f
end.inject(:+)
end
end

def transform_raw_value(raw_value, entry, opts = {})
raw_value ||= {}

{}.tap do |h|
columns_array.each_with_index do |column, index|
h[column] = raw_value[index.to_s]
end

# Iterate through each row, and remove ones without any values.
i = h.first[1].length - 1
while i >= 0
unless h.find { |k, v| v[i].present? }
h.each do |k, v|
v.delete_at(i)
end
end

i = i - 1
end
end
end

end
end
4 changes: 2 additions & 2 deletions spec/controllers/formbuilder/forms_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@

it 'should remove existing fields' do
form = FactoryGirl.create(:kitchen_sink_form)
form.response_fields.count.should == 14
form.response_fields.count.should == 15
put :update, id: form.id, fields: JSON.parse(TEST_JSON)
form.reload.response_fields.count.should == 3
end

it 'should preserve existing fields' do
form = FactoryGirl.create(:kitchen_sink_form)
form.response_fields.count.should == 14
form.response_fields.count.should == 15
json_payload = JSON.parse(TEST_JSON)
json_payload[0]['id'] = form.response_fields.first.id
put :update, id: form.id, fields: json_payload
Expand Down
3 changes: 3 additions & 0 deletions spec/factories/formbuilder_forms.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
f.response_fields.create(label: "File", type: "Formbuilder::ResponseFieldFile", sort_order: 11, field_options: {"required" => true})
f.response_fields.create(label: "Email", type: "Formbuilder::ResponseFieldEmail", sort_order: 12, field_options: {"required" => true})
f.response_fields.create(label: "Address", type: "Formbuilder::ResponseFieldAddress", sort_order: 13, field_options: {"required" => true})
f.response_fields.create(label: "Table", type: "Formbuilder::ResponseFieldTable", sort_order: 14,
field_options: {"columns" => [{'label' => 'column one'}, {'label' => 'column two'}],
"minrows" => "2"})
end
end

Expand Down
81 changes: 81 additions & 0 deletions spec/models/formbuilder/response_field_table_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
require 'spec_helper'

describe Formbuilder::ResponseFieldTable do

let(:response_field) do
Formbuilder::ResponseFieldTable.new(
field_options: { 'columns' => [{'label' => 'column one'}] }
)
end
subject { response_field }
subject(:rf) { response_field }

describe '#normalize_response' do
it 'calculates the individual sortable values' do
all_responses = {}
rf.normalize_response({'column one' => ['1', '2', '3.5', 'asdf']}, all_responses)
all_responses["#{rf.id}_sum_column one"].should eq 6.5
end

it 'calculates the sums' do
all_responses = {}
rf.normalize_response({'column one' => ['asdfasdf', 'fasdf']}, all_responses)
all_responses["#{rf.id}_sum_column one"].should eq 0
end
end

describe '#transform_raw_value' do
let(:entry) { Entry.new }

before do
response_field.update_attributes(
field_options: { 'columns' => [{'label' => 'column one'},
{'label' => 'column two'}] }
)
end

it 'transforms the options properly' do
transformed = rf.transform_raw_value({ '0' => ['foo', 'bar'] }, entry)
transformed['column one'].should == ['foo', 'bar']
end

it 'preserves rows with one input' do
transformed = rf.transform_raw_value({ '0' => ['', 'bar'], '1' => ['baz', ''] }, entry)
transformed['column one'].should == ['', 'bar']
transformed['column two'].should == ['baz', '']

end

it 'removes blank rows' do
transformed = rf.transform_raw_value({ '0' => ['', 'bar'], '1' => ['', 'baz'] }, entry)
transformed['column one'].should == ['bar']
transformed['column two'].should == ['baz']
end

# it 'transforms the other option' do
# transformed = rf.transform_raw_value({ 'other_checkbox' => 'on', 'other' => 'bar' }, entry)
# transformed['Other'].should == 'bar'
# end

# it 'does not save the other option unless the checkbox is checked' do
# transformed = rf.transform_raw_value({ 'other_checkbox' => nil, 'other' => 'bar' }, entry)
# transformed['Other'].should == nil
# end

# it 'sets the _present attribute' do
# rf.transform_raw_value({ '0' => 'on' }, entry)
# entry.responses["#{rf.id}_present"].should == true
# rf.transform_raw_value({ '0' => nil }, entry)
# entry.responses["#{rf.id}_present"].should == nil
# rf.transform_raw_value({ 'other_checkbox' => 'on', 'other' => 'z' }, entry)
# entry.responses["#{rf.id}_present"].should == true
# end
end

describe '#render_entry_text' do
it 'functions properly' do
expect(rf.render_entry_text({'column one' => ['bar', 'baz']})).to eq 'column one: bar, baz'
end
end

end
18 changes: 16 additions & 2 deletions spec/support/submitting_an_entry_spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ def set_field(field_type, value)
fill_in "#{field_name(field_type)}[state]", with: value[:state]
fill_in "#{field_name(field_type)}[zipcode]", with: value[:zipcode]
select(value[:country], from: "#{field_name(field_type)}[country]")
when :table
value.each_with_index do |values, i|
all("input").select { |input| input['name']["#{field_name(field_type)}[#{i}]"] }.each_with_index do |input, index|
input.set values[index]
end
end
else
fill_in field_name(field_type), with: value
end
Expand All @@ -65,7 +71,8 @@ def test_field_values
website: 'www.google.com',
email: 'strongbad@homestarrunner.com',
file: '../fixtures/test_files/text.txt',
address: { street: '123 Main St.', city: 'Oakland', state: 'California', zipcode: '94609', country: 'United Kingdom' }
address: { street: '123 Main St.', city: 'Oakland', state: 'California', zipcode: '94609', country: 'United Kingdom' },
table: [['yes', 'no'], ['', 'foo']]
}
end

Expand All @@ -89,7 +96,8 @@ def test_field_values_two
website: 'www.gizoogle.com',
email: 'homestar@homestarrunner.com',
file: '../fixtures/test_files/text2.txt',
address: { street: '125 Main St.', city: 'Berkeley', state: 'California', zipcode: '94704', country: 'Algeria' }
address: { street: '125 Main St.', city: 'Berkeley', state: 'California', zipcode: '94704', country: 'Algeria' },
table: [['no', ''], ['yes', '']]
}
end

Expand Down Expand Up @@ -129,6 +137,12 @@ def ensure_field(field_type, value)
page.should have_field("#{field_name(field_type)}[state]", with: value[:state])
page.should have_field("#{field_name(field_type)}[zipcode]", with: value[:zipcode])
page.should have_select("#{field_name(field_type)}[country]", selected: value[:country])
when :table
value.each_with_index do |values, i|
all("input").select { |input| input['name']["#{field_name(field_type)}[#{i}]"] }.each_with_index do |input, index|
input.value.should == values[index]
end
end
else
page.should have_field(field_name(field_type), with: value)
end
Expand Down

0 comments on commit 8d20b6f

Please sign in to comment.