bootstrap_form_extensions 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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