bootstrap_form_extensions 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Rakefile +34 -0
  4. data/app/assets/javascripts/bootstrap_form_extensions/arrayed_field.js +70 -0
  5. data/app/assets/javascripts/bootstrap_form_extensions/bootstrap-timepicker.min.js +5 -0
  6. data/app/assets/javascripts/bootstrap_form_extensions/index.js +2 -0
  7. data/app/assets/javascripts/bootstrap_form_extensions/jquery.html5data.min.js +7 -0
  8. data/app/assets/javascripts/bootstrap_form_extensions/scheduler.js +226 -0
  9. data/app/assets/javascripts/bootstrap_form_extensions/select_or_new.js +76 -0
  10. data/app/assets/javascripts/bootstrap_form_extensions/time_picker.js +61 -0
  11. data/app/assets/javascripts/bootstrap_form_extensions/timespan.js +60 -0
  12. data/app/assets/stylesheets/bootstrap_form_extensions/bootstrap-timepicker.min.css +10 -0
  13. data/app/assets/stylesheets/bootstrap_form_extensions/common.css +11 -0
  14. data/app/assets/stylesheets/bootstrap_form_extensions/index.css +4 -0
  15. data/app/assets/stylesheets/bootstrap_form_extensions/scheduler.css +31 -0
  16. data/app/assets/stylesheets/bootstrap_form_extensions/submit_bar.css +4 -0
  17. data/app/views/bootstrap_form_extensions/_submit_bar.html.erb +36 -0
  18. data/lib/bootstrap_form_extensions.rb +22 -0
  19. data/lib/bootstrap_form_extensions/arrayed_field.rb +107 -0
  20. data/lib/bootstrap_form_extensions/date_time_pickers.rb +71 -0
  21. data/lib/bootstrap_form_extensions/helpers.rb +18 -0
  22. data/lib/bootstrap_form_extensions/scheduler.rb +72 -0
  23. data/lib/bootstrap_form_extensions/select_or_new.rb +36 -0
  24. data/lib/bootstrap_form_extensions/submit_bar.rb +65 -0
  25. data/lib/bootstrap_form_extensions/timespan.rb +55 -0
  26. data/lib/bootstrap_form_extensions/version.rb +3 -0
  27. data/lib/tasks/rails_bootstrap_form_extensions_tasks.rake +4 -0
  28. data/test/arrayed_field_test.rb +63 -0
  29. data/test/bootstrap_form_extensions_test.rb +7 -0
  30. data/test/date_time_pickers_test.rb +50 -0
  31. data/test/dummy/README.rdoc +28 -0
  32. data/test/dummy/Rakefile +6 -0
  33. data/test/dummy/app/assets/javascripts/application.js +13 -0
  34. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  35. data/test/dummy/app/controllers/application_controller.rb +5 -0
  36. data/test/dummy/app/helpers/application_helper.rb +2 -0
  37. data/test/dummy/app/models/category.rb +7 -0
  38. data/test/dummy/app/models/thing.rb +46 -0
  39. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  40. data/test/dummy/bin/bundle +3 -0
  41. data/test/dummy/bin/rails +4 -0
  42. data/test/dummy/bin/rake +4 -0
  43. data/test/dummy/bin/setup +29 -0
  44. data/test/dummy/config.ru +4 -0
  45. data/test/dummy/config/application.rb +26 -0
  46. data/test/dummy/config/boot.rb +5 -0
  47. data/test/dummy/config/database.yml +25 -0
  48. data/test/dummy/config/environment.rb +5 -0
  49. data/test/dummy/config/environments/development.rb +41 -0
  50. data/test/dummy/config/environments/production.rb +79 -0
  51. data/test/dummy/config/environments/test.rb +42 -0
  52. data/test/dummy/config/initializers/assets.rb +11 -0
  53. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  54. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  55. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  56. data/test/dummy/config/initializers/inflections.rb +16 -0
  57. data/test/dummy/config/initializers/mime_types.rb +4 -0
  58. data/test/dummy/config/initializers/session_store.rb +3 -0
  59. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  60. data/test/dummy/config/locales/en.yml +23 -0
  61. data/test/dummy/config/routes.rb +3 -0
  62. data/test/dummy/config/secrets.yml +22 -0
  63. data/test/dummy/db/migrate/20150918185031_create_things.rb +9 -0
  64. data/test/dummy/db/migrate/20150924203053_add_timespan.rb +7 -0
  65. data/test/dummy/db/migrate/20150929213249_add_arrayed_field.rb +7 -0
  66. data/test/dummy/db/migrate/20151006171627_add_another_arrayed_field.rb +7 -0
  67. data/test/dummy/db/migrate/20151006181943_add_json_field.rb +7 -0
  68. data/test/dummy/db/migrate/20151007213131_add_scheduler_field.rb +7 -0
  69. data/test/dummy/db/migrate/20151030194330_add_date_time.rb +7 -0
  70. data/test/dummy/db/migrate/20151106165522_add_category.rb +10 -0
  71. data/test/dummy/db/schema.rb +31 -0
  72. data/test/dummy/db/test.sqlite3 +0 -0
  73. data/test/dummy/log/development.log +0 -0
  74. data/test/dummy/log/test.log +1997 -0
  75. data/test/dummy/public/404.html +67 -0
  76. data/test/dummy/public/422.html +67 -0
  77. data/test/dummy/public/500.html +66 -0
  78. data/test/dummy/public/favicon.ico +0 -0
  79. data/test/dummy/test/fixtures/things.yml +8 -0
  80. data/test/javascripts/arrayed_field_spec.js +101 -0
  81. data/test/javascripts/helpers/function_bind_polyfill_for_phantomjs.js +32 -0
  82. data/test/javascripts/helpers/jasmine-jquery.js +838 -0
  83. data/test/javascripts/scheduler_spec.js +354 -0
  84. data/test/javascripts/select_or_new_spec.js +113 -0
  85. data/test/javascripts/support/jasmine.yml +127 -0
  86. data/test/javascripts/support/jasmine_helper.rb +19 -0
  87. data/test/javascripts/time_picker_spec.js +42 -0
  88. data/test/javascripts/timespan_spec.js +81 -0
  89. data/test/javascripts/vendor/bootstrap.min.js +7 -0
  90. data/test/javascripts/vendor/jquery-2.1.4.min.js +4 -0
  91. data/test/scheduler_serializer_test.rb +200 -0
  92. data/test/scheduler_test.rb +15 -0
  93. data/test/select_or_new_test.rb +37 -0
  94. data/test/submit_bar_test.rb +112 -0
  95. data/test/test_helper.rb +37 -0
  96. data/test/timespan_test.rb +47 -0
  97. metadata +291 -0
@@ -0,0 +1,72 @@
1
+ module BootstrapFormExtensions
2
+
3
+ module Scheduler
4
+
5
+ include BootstrapFormExtensions::Helpers
6
+
7
+ def self.serializer to: :yaml, default_selection: true
8
+ BootstrapFormExtensions::Scheduler::Serializer.new to, default_selection
9
+ end
10
+
11
+ def scheduler method, **options
12
+ schedule = object.send method
13
+ hidden = hidden_field method, value: JSON.dump(schedule), class: 'scheduler-hidden-field'
14
+ options[:wrapper] ||= {}
15
+ options[:wrapper][:data] ||= {}
16
+ options[:wrapper][:data][:scheduler] = true
17
+ form_group_builder(method, options) { hidden + schedule_to_table(schedule) }
18
+ end
19
+
20
+ private
21
+
22
+ def schedule_to_table schedule
23
+ content_tag :table, class: 'scheduler-badge' do
24
+ content_tag :tbody do
25
+ schedule.each.with_index.inject(''.html_safe) do |rows, (day_array, day_index)|
26
+ rows << content_tag(:tr) do
27
+ day_array.each.with_index.inject(''.html_safe) do |columns, (value, hour_index)|
28
+ columns << content_tag(:td, '&nbsp;'.html_safe, class: (true?(value) ? 'on' : 'off'), data: { day: day_index, hour: hour_index })
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ class Serializer
37
+
38
+ include BootstrapFormExtensions::Helpers
39
+
40
+ attr_reader :to_format, :default_selection
41
+
42
+ def initialize to_format, default_selection
43
+ @to_format = to_format
44
+ @default_selection = default_selection
45
+ end
46
+
47
+ def load value_from_db
48
+ value_from_db = YAML.load(value_from_db.to_s || '') if to_format == :yaml
49
+ value_from_db = [] if value_from_db.blank?
50
+ value_from_db = parse_schedule_array value_from_db unless value_from_db[6].try(:[], 23).present?
51
+ value_from_db
52
+ end
53
+
54
+ def dump value_from_user
55
+ value_from_user = JSON.parse value_from_user rescue [] if value_from_user.is_a? String
56
+ value_from_user = [] unless value_from_user.is_a? Array
57
+ value_from_user = parse_schedule_array value_from_user
58
+ value_from_user = value_from_user.to_yaml if to_format == :yaml
59
+ value_from_user
60
+ end
61
+
62
+ private
63
+
64
+ def parse_schedule_array value
65
+ Array.new(7) { |i| Array.new(24) { |j| (value[i][j].nil? && default_selection) || true?(value[i][j]) rescue default_selection } }
66
+ end
67
+
68
+ end
69
+
70
+ end
71
+
72
+ end
@@ -0,0 +1,36 @@
1
+ module BootstrapFormExtensions
2
+
3
+ module SelectOrNew
4
+
5
+ include BootstrapFormExtensions::Helpers
6
+
7
+ def select_or_new method, choices = [], options = {}, html_options = {}
8
+ new_is_selected = object.send(method) == 0
9
+
10
+ # select...
11
+ options.delete :prompt
12
+ options.delete :include_blank
13
+ choices.unshift [ "Please select", nil ]
14
+ choices.push [ "New...", 0 ]
15
+ html_options[:class] = [ "form-control", html_options[:class] ].compact.join(' ')
16
+ html_options[:style] = 'display: none;' if new_is_selected
17
+ select = self.select_without_bootstrap method, choices, options, html_options
18
+
19
+ # ... or new
20
+ icon = @template.glyphicon_tag 'remove', class: 'text-danger'
21
+ icon = content_tag :div, icon, class: 'input-group-addon select-or-new-cancel'
22
+ new_method = "new_#{method.to_s.sub(/_id$/, '')}"
23
+ new_field_name = "#{object_name}[#{new_method}]"
24
+ text = @template.text_field_tag new_field_name, object.try(new_method), class: 'form-control', placeholder: 'New...'
25
+ text = content_tag :div, text + icon, class: 'input-group', style: (new_is_selected ? '' : 'display: none;')
26
+
27
+ # form group to put them together
28
+ html_options[:wrapper] ||= {}
29
+ html_options[:wrapper][:data] ||= {}
30
+ html_options[:wrapper][:data][:select_or_new] = true
31
+ form_group_builder(method, options, html_options) { select + text }
32
+ end
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,65 @@
1
+ module BootstrapFormExtensions
2
+
3
+ module SubmitBar
4
+
5
+ mattr_accessor :partial
6
+ @@partial = 'bootstrap_form_extensions/submit_bar'
7
+
8
+ mattr_accessor :show_submit_button
9
+ @@show_submit_button = true
10
+
11
+ mattr_accessor :submit_button_text
12
+ @@submit_button_text = 'Save'
13
+
14
+ mattr_accessor :show_submit_menu
15
+ @@show_submit_menu = true
16
+
17
+ mattr_accessor :show_submit_and_dup_button
18
+ @@show_submit_and_dup_button = true
19
+
20
+ mattr_accessor :show_submit_and_new_button
21
+ @@show_submit_and_new_button = true
22
+
23
+ mattr_accessor :show_cancel_button
24
+ @@show_cancel_button = true
25
+
26
+ mattr_accessor :cancel_button_text
27
+ @@cancel_button_text = 'Cancel'
28
+
29
+ mattr_accessor :back_button_text
30
+ @@back_button_text = 'Back'
31
+
32
+ def submit_bar show_submit_button: @@show_submit_button, submit_button_text: @@submit_button_text,
33
+ show_submit_menu: @@show_submit_menu, show_submit_and_dup_button: @@show_submit_and_dup_button, show_submit_and_new_button: @@show_submit_and_new_button,
34
+ show_cancel_button: @@show_cancel_button, cancel_button_text: nil, cancel_button_url: nil,
35
+ extra_buttons: [], right_buttons: [],
36
+ partial: @@partial
37
+
38
+ show_submit_menu &&= @template.current_ability.can? :new, object if @template.respond_to? :current_ability
39
+ cancel_button_url ||= @template.url_for(object.persisted? ? object : @template.controller_name.to_sym) rescue 'javascript:history.back();'
40
+ cancel_button_text ||= show_submit_button ? @@cancel_button_text : @@back_button_text
41
+
42
+ @template.render partial, {
43
+ show_submit_button: show_submit_button, submit_button_text: submit_button_text,
44
+ show_submit_menu: show_submit_menu, show_submit_and_dup_button: show_submit_and_dup_button, show_submit_and_new_button: show_submit_and_new_button,
45
+ show_cancel_button: show_cancel_button, cancel_button_text: cancel_button_text, cancel_button_url: cancel_button_url,
46
+ extra_buttons: parse_extra_buttons_for_submit_bar(extra_buttons, :extra),
47
+ right_buttons: parse_extra_buttons_for_submit_bar(right_buttons, :right),
48
+ }
49
+ end
50
+
51
+ private
52
+
53
+ def parse_extra_buttons_for_submit_bar buttons, type
54
+ buttons.map do |options|
55
+ text = options.delete :text
56
+ url = options.delete :url
57
+ options[:class] = [ 'btn', 'btn-default', "submitbar-#{type}-button", options[:class] ].compact.join(' ')
58
+ options[:rel] ||= "nofollow"
59
+ { text: text, url: url, options: options }
60
+ end
61
+ end
62
+
63
+ end
64
+
65
+ end
@@ -0,0 +1,55 @@
1
+ module BootstrapFormExtensions
2
+
3
+ module Timespan
4
+
5
+ UNITS_IN_SECONDS = ActiveSupport::OrderedHash[ :seconds, 1, :minutes, 60, :hours, 3600, :days, 86400, :weeks, 604800, :months, 18144000 ]
6
+
7
+ mattr_accessor :units
8
+ @@units = UNITS_IN_SECONDS.keys
9
+
10
+ def timespan method, units: @@units, quantity_options: {}, unit_options: {}
11
+ quantity, selected, units = quantity_and_units_for_timespan method, units
12
+ hidden = hidden_field method, class: 'timespan-seconds'
13
+ field = text_field_for_timespan method, quantity, quantity_options
14
+ select = select_for_timespan method, selected, units, unit_options
15
+ content_tag :span, hidden + field + '&nbsp;'.html_safe + select, data: { timespan: true }
16
+ end
17
+
18
+ private
19
+
20
+ def quantity_and_units_for_timespan method, units
21
+ units = units.map { |unit| [ unit, UNITS_IN_SECONDS[unit] ] }
22
+
23
+ time_in_seconds = object.send method
24
+ return [ '', units.first.last, units ] if time_in_seconds.blank?
25
+
26
+ selected = UNITS_IN_SECONDS.values.reverse.find(proc { 1 }) { |seconds| time_in_seconds % seconds == 0 }
27
+ quantity = time_in_seconds / selected
28
+ [ quantity, selected, units ]
29
+ end
30
+
31
+ def text_field_for_timespan method, quantity, quantity_options
32
+ text_field_name = method.to_s.sub(/(_in_seconds)?$/, '_quantity').to_sym
33
+
34
+ quantity_options[:size] ||= 5
35
+ quantity_options[:class] = [ "form-control", "timespan-quantity", quantity_options[:class] ].compact.join(' ')
36
+
37
+ field = @template.text_field_tag text_field_name, quantity, quantity_options
38
+ field << generate_help(method, nil)
39
+
40
+ css_class = 'form-group'
41
+ css_class << " #{error_class}" if has_error?(method)
42
+
43
+ content_tag :div, field, class: css_class
44
+ end
45
+
46
+ def select_for_timespan method, selected, units, unit_options
47
+ select_field_name = method.to_s.sub(/(_in_seconds)?$/, '_unit').to_sym
48
+ unit_options[:class] = [ "form-control", "timespan-unit", unit_options[:class] ].compact.join(' ')
49
+ select = @template.select_tag select_field_name, @template.options_for_select(units, selected), unit_options
50
+ content_tag :div, select, class: 'form-group'
51
+ end
52
+
53
+ end
54
+
55
+ end
@@ -0,0 +1,3 @@
1
+ module BootstrapFormExtensions
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :bootstrap_form_extensions do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,63 @@
1
+ require 'test_helper'
2
+
3
+ class ArrayedFieldTest < ActionView::TestCase
4
+ include BootstrapFormExtensions::ArrayedField
5
+
6
+ def setup
7
+ setup_test_fixture
8
+ end
9
+
10
+ test "arrayed_text_field with default values" do
11
+ expected = '<div class="form-group"><label class="control-label col-sm-2" for="thing_list">List</label><div class="col-sm-10"><div data-arrayed-field="true"><div class="blueprint-for-arrayed-field" style="display:none;"><div class="row voffset1"><div class="col-sm-11"><input type="text" id="" class="form-control" data-name="thing[list][]" /></div><div class="col-sm-1"><a class="btn btn-default remove-arrayed-field-row" href="javascript:void(0);"><i class=" glyphicon glyphicon-trash"></i></a></div></div></div><div class="arrayed-field-rows"><div class="row voffset1"><div class="col-sm-11"><input type="text" name="thing[list][]" id="thing_list_" value="One" class="form-control" /></div><div class="col-sm-1"><a class="btn btn-default remove-arrayed-field-row" href="javascript:void(0);"><i class=" glyphicon glyphicon-trash"></i></a></div></div><div class="row voffset1"><div class="col-sm-11"><input type="text" name="thing[list][]" id="thing_list_" value="Two" class="form-control" /></div><div class="col-sm-1"><a class="btn btn-default remove-arrayed-field-row" href="javascript:void(0);"><i class=" glyphicon glyphicon-trash"></i></a></div></div></div><div class="row voffset1"><div class="col-sm-12"><a class="btn btn-default add-arrayed-field-row" href="javascript:void(0);"><i class=" glyphicon glyphicon-plus"></i></a></div></div></div></div></div>'
12
+ assert_equal expected, @builder.arrayed_text_field(:list)
13
+ end
14
+
15
+ test "arrayed_url_field with default values" do
16
+ expected = '<div class="form-group"><label class="control-label col-sm-2" for="thing_urls">Urls</label><div class="col-sm-10"><div data-arrayed-field="true"><div class="blueprint-for-arrayed-field" style="display:none;"><div class="row voffset1"><div class="col-sm-11"><input type="url" id="" class="form-control" data-name="thing[urls][]" /></div><div class="col-sm-1"><a class="btn btn-default remove-arrayed-field-row" href="javascript:void(0);"><i class=" glyphicon glyphicon-trash"></i></a></div></div></div><div class="arrayed-field-rows"><div class="row voffset1"><div class="col-sm-11"><input type="url" name="thing[urls][]" id="thing_urls_" value="www.example1.com" class="form-control" /></div><div class="col-sm-1"><a class="btn btn-default remove-arrayed-field-row" href="javascript:void(0);"><i class=" glyphicon glyphicon-trash"></i></a></div></div><div class="row voffset1"><div class="col-sm-11"><input type="url" name="thing[urls][]" id="thing_urls_" value="www.example2.com" class="form-control" /></div><div class="col-sm-1"><a class="btn btn-default remove-arrayed-field-row" href="javascript:void(0);"><i class=" glyphicon glyphicon-trash"></i></a></div></div></div><div class="row voffset1"><div class="col-sm-12"><a class="btn btn-default add-arrayed-field-row" href="javascript:void(0);"><i class=" glyphicon glyphicon-plus"></i></a></div></div></div></div></div>'
17
+ assert_equal expected, @builder.arrayed_url_field(:urls)
18
+ end
19
+
20
+ test "ArrayedField, with nonexistent attribute, assumes the value is an empty array" do
21
+ expected = '<div class="form-group"><label class="control-label col-sm-2" for="thing_nonexistent">Nonexistent</label><div class="col-sm-10"><div data-arrayed-field="true"><div class="blueprint-for-arrayed-field" style="display:none;"><div class="row voffset1"><div class="col-sm-11"><input type="text" id="" class="form-control" data-name="thing[nonexistent][]" /></div><div class="col-sm-1"><a class="btn btn-default remove-arrayed-field-row" href="javascript:void(0);"><i class=" glyphicon glyphicon-trash"></i></a></div></div></div><div class="arrayed-field-rows"></div><div class="row voffset1"><div class="col-sm-12"><a class="btn btn-default add-arrayed-field-row" href="javascript:void(0);"><i class=" glyphicon glyphicon-plus"></i></a></div></div></div></div></div>'
22
+ assert_equal expected, @builder.arrayed_text_field(:nonexistent)
23
+ end
24
+
25
+ test "ArrayedField, with non-array attribute, assumes the value is an empty array" do
26
+ expected = '<div class="form-group"><label class="control-label col-sm-2 required" for="thing_name">Name</label><div class="col-sm-10"><div data-arrayed-field="true"><div class="blueprint-for-arrayed-field" style="display:none;"><div class="row voffset1"><div class="col-sm-11"><input type="text" id="" class="form-control" data-name="thing[name][]" /></div><div class="col-sm-1"><a class="btn btn-default remove-arrayed-field-row" href="javascript:void(0);"><i class=" glyphicon glyphicon-trash"></i></a></div></div></div><div class="arrayed-field-rows"></div><div class="row voffset1"><div class="col-sm-12"><a class="btn btn-default add-arrayed-field-row" href="javascript:void(0);"><i class=" glyphicon glyphicon-plus"></i></a></div></div></div></div></div>'
27
+ assert_equal expected, @builder.arrayed_text_field(:name)
28
+ end
29
+
30
+ test "pass options to the form group in the an ArrayedField" do
31
+ @output_buffer = @builder.arrayed_text_field(:list, wrapper_class: 'extra-class')
32
+ assert_select 'div.form-group.extra-class'
33
+ end
34
+
35
+ test "arrayed_json_field with default values" do
36
+ expected = '<div class="form-group"><label class="control-label col-sm-2" for="thing_variables">Variables</label><div class="col-sm-10"><div data-arrayed-field="true"><div class="blueprint-for-arrayed-field" style="display:none;"><div class="row voffset1"><div class="col-sm-2"><input type="text" id="" class="form-control" placeholder="name" data-name="thing[variables][][name]" /></div><div class="col-sm-2"><input type="text" id="" class="form-control" placeholder="value" data-name="thing[variables][][value]" /></div><div class="col-sm-1"><a class="btn btn-default remove-arrayed-field-row" href="javascript:void(0);"><i class=" glyphicon glyphicon-trash"></i></a></div></div></div><div class="arrayed-field-rows"><div class="row voffset1"><div class="col-sm-2"><input type="text" name="thing[variables][][name]" id="thing_variables__name" class="form-control" placeholder="name" /></div><div class="col-sm-2"><input type="text" name="thing[variables][][value]" id="thing_variables__value" class="form-control" placeholder="value" /></div><div class="col-sm-1"><a class="btn btn-default remove-arrayed-field-row" href="javascript:void(0);"><i class=" glyphicon glyphicon-trash"></i></a></div></div><div class="row voffset1"><div class="col-sm-2"><input type="text" name="thing[variables][][name]" id="thing_variables__name" class="form-control" placeholder="name" /></div><div class="col-sm-2"><input type="text" name="thing[variables][][value]" id="thing_variables__value" class="form-control" placeholder="value" /></div><div class="col-sm-1"><a class="btn btn-default remove-arrayed-field-row" href="javascript:void(0);"><i class=" glyphicon glyphicon-trash"></i></a></div></div></div><div class="row voffset1"><div class="col-sm-12"><a class="btn btn-default add-arrayed-field-row" href="javascript:void(0);"><i class=" glyphicon glyphicon-plus"></i></a></div></div></div></div></div>'
37
+ assert_equal expected, @builder.arrayed_json_field(:variables, %i[ name value ])
38
+ end
39
+
40
+ test "arrayed_json_field with custom input types" do
41
+ expected = <<-HTML.chomp
42
+ <div class="form-group"><label class="control-label col-sm-2" for="thing_variables">Variables</label><div class="col-sm-10"><div data-arrayed-field="true"><div class="blueprint-for-arrayed-field" style="display:none;"><div class="row voffset1"><div class="col-sm-2"><select id="" class="form-control" data-name="thing[variables][][name]"><option value="var1">Variable One</option>
43
+ <option value="var2">Variable Two</option>
44
+ <option value="var3">Variable Three</option></select></div><div class="col-sm-2"><input type="text" id="" class="form-control" placeholder="value" data-name="thing[variables][][value]" /></div><div class="col-sm-1"><a class="btn btn-default remove-arrayed-field-row" href="javascript:void(0);"><i class=" glyphicon glyphicon-trash"></i></a></div></div></div><div class="arrayed-field-rows"><div class="row voffset1"><div class="col-sm-2"><select id="" class="form-control"><option value="var1">Variable One</option>
45
+ <option value="var2">Variable Two</option>
46
+ <option value="var3">Variable Three</option></select></div><div class="col-sm-2"><input type="text" name="thing[variables][][value]" id="thing_variables__value" class="form-control" placeholder="value" /></div><div class="col-sm-1"><a class="btn btn-default remove-arrayed-field-row" href="javascript:void(0);"><i class=" glyphicon glyphicon-trash"></i></a></div></div><div class="row voffset1"><div class="col-sm-2"><select id="" class="form-control"><option value="var1">Variable One</option>
47
+ <option value="var2">Variable Two</option>
48
+ <option value="var3">Variable Three</option></select></div><div class="col-sm-2"><input type="text" name="thing[variables][][value]" id="thing_variables__value" class="form-control" placeholder="value" /></div><div class="col-sm-1"><a class="btn btn-default remove-arrayed-field-row" href="javascript:void(0);"><i class=" glyphicon glyphicon-trash"></i></a></div></div></div><div class="row voffset1"><div class="col-sm-12"><a class="btn btn-default add-arrayed-field-row" href="javascript:void(0);"><i class=" glyphicon glyphicon-plus"></i></a></div></div></div></div></div>
49
+ HTML
50
+ assert_equal expected, @builder.arrayed_json_field(:variables, [ { name: { type: :select, options: options_for_variables } }, :value ])
51
+ end
52
+
53
+ private
54
+
55
+ def options_for_variables
56
+ [
57
+ [ "Variable One", "var1" ],
58
+ [ "Variable Two", "var2" ],
59
+ [ "Variable Three", "var3" ],
60
+ ]
61
+ end
62
+
63
+ end
@@ -0,0 +1,7 @@
1
+ require 'test_helper'
2
+
3
+ class BootstrapFormExtensionsTest < ActiveSupport::TestCase
4
+ test "truth" do
5
+ assert_kind_of Module, BootstrapFormExtensions
6
+ end
7
+ end
@@ -0,0 +1,50 @@
1
+ require 'test_helper'
2
+
3
+ class DateTimePickersTest < ActionView::TestCase
4
+ include BootstrapFormExtensions::DateTimePickers
5
+
6
+ def setup
7
+ setup_test_fixture
8
+ end
9
+
10
+ test "date_picker" do
11
+ expected = '<div class="form-group"><label class="control-label col-sm-2" for="thing_start_date">Start date</label><div class="col-sm-10"><div class="input-group date" data-provide="datepicker" data-date-today-highlight="true" data-date-format="yyyy-mm-dd" data-date-today-btn="linked" data-date-autoclose="true" data-date-disable-touch-keyboard="true" data-date-enable-on-readonly="false" data-date-show-on-focus="false"><input size="10" class="form-control" type="text" value="1971-10-21" name="thing[start_date]" id="thing_start_date" /><span class="input-group-addon"><i class=" glyphicon glyphicon-calendar"></i></span></div></div></div>'
12
+ assert_equal expected, @builder.date_picker(:start_date)
13
+ end
14
+
15
+ test "add an option to date_picker" do
16
+ @output_buffer = @builder.date_picker :start_date, widget: { clear_btn: true }
17
+ assert_select 'div.date[data-date-clear-btn=true]'
18
+ end
19
+
20
+ test "overwrite one of the default options to date_picker" do
21
+ @output_buffer = @builder.date_picker :start_date, widget: { format: 'dd-mm-yyyy' }
22
+ assert_select 'div.date[data-date-format="dd-mm-yyyy"]'
23
+ end
24
+
25
+ test "time_picker" do
26
+ expected = '<div class="form-group"><label class="control-label col-sm-2" for="thing_start_time">Start time</label><div class="col-sm-10"><div class="input-group bootstrap-timepicker"><input class="form-control" size="8" data-provide="timepicker" type="text" value="07:30:00" name="thing[start_time]" id="thing_start_time" /><span class="input-group-addon"><i class=" glyphicon glyphicon-time"></i></span></div></div></div>'
27
+ assert_equal expected, @builder.time_picker(:start_time)
28
+ end
29
+
30
+ test "add an option to time_picker" do
31
+ @output_buffer = @builder.time_picker :start_time, widget: { disable_mousewheel: true }
32
+ assert_select 'input[data-time-disable-mousewheel=true]'
33
+ end
34
+
35
+ test "date_time_picker" do
36
+ expected = '<div class="form-group"><label class="control-label col-sm-2" for="thing_start_at">Start at</label><div class="col-sm-10 form-inline"><div class="form-group"><div class="input-group date" data-provide="datepicker" data-date-today-highlight="true" data-date-format="yyyy-mm-dd" data-date-today-btn="linked" data-date-autoclose="true" data-date-disable-touch-keyboard="true" data-date-enable-on-readonly="false" data-date-show-on-focus="false"><input size="10" class="form-control" type="text" value="1971-10-21" name="thing[start_date]" id="thing_start_date" /><span class="input-group-addon"><i class=" glyphicon glyphicon-calendar"></i></span></div></div>&nbsp;&nbsp;<div class="form-group"><div class="input-group bootstrap-timepicker"><input class="form-control" size="8" data-provide="timepicker" type="text" value="07:30:00" name="thing[start_time]" id="thing_start_time" /><span class="input-group-addon"><i class=" glyphicon glyphicon-time"></i></span></div></div></div></div>'
37
+ assert_equal expected, @builder.date_time_picker(:start_at)
38
+ end
39
+
40
+ test "add an option to the date control in the date_time_picker" do
41
+ @output_buffer = @builder.date_time_picker :start_at, date_options: { widget: { clear_btn: true } }
42
+ assert_select 'div.date[data-date-clear-btn=true]'
43
+ end
44
+
45
+ test "add an option to the time control in the date_time_picker" do
46
+ @output_buffer = @builder.date_time_picker :start_at, time_options: { widget: { disable_mousewheel: true } }
47
+ assert_select 'input[data-time-disable-mousewheel=true]'
48
+ end
49
+
50
+ end
@@ -0,0 +1,28 @@
1
+ == README
2
+
3
+ This README would normally document whatever steps are necessary to get the
4
+ application up and running.
5
+
6
+ Things you may want to cover:
7
+
8
+ * Ruby version
9
+
10
+ * System dependencies
11
+
12
+ * Configuration
13
+
14
+ * Database creation
15
+
16
+ * Database initialization
17
+
18
+ * How to run the test suite
19
+
20
+ * Services (job queues, cache servers, search engines, etc.)
21
+
22
+ * Deployment instructions
23
+
24
+ * ...
25
+
26
+
27
+ Please feel free to use a different markup language if you do not plan to run
28
+ <tt>rake doc:app</tt>.
@@ -0,0 +1,6 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require File.expand_path('../config/application', __FILE__)
5
+
6
+ Rails.application.load_tasks
@@ -0,0 +1,13 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file.
9
+ //
10
+ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require_tree .
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any styles
10
+ * defined in the other CSS/SCSS files in this directory. It is generally better to create a new
11
+ * file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,5 @@
1
+ class ApplicationController < ActionController::Base
2
+ # Prevent CSRF attacks by raising an exception.
3
+ # For APIs, you may want to use :null_session instead.
4
+ protect_from_forgery with: :exception
5
+ end