diff options
author | Carlos Galdino <carloshsgaldino@gmail.com> | 2012-05-16 18:24:16 -0300 |
---|---|---|
committer | Carlos Galdino <carloshsgaldino@gmail.com> | 2012-05-21 17:23:56 -0300 |
commit | b02d14aad515a039c284c93a68845503dc1658e2 (patch) | |
tree | 929dc85a9b266cb89ec6280e535a2a3ef5d93f43 | |
parent | 1ce4b5b33d694eeebdb0495aef70d8d1b50cd02d (diff) | |
download | rails-b02d14aad515a039c284c93a68845503dc1658e2.tar.gz rails-b02d14aad515a039c284c93a68845503dc1658e2.tar.bz2 rails-b02d14aad515a039c284c93a68845503dc1658e2.zip |
Add several HTML5 input helpers
The input types added are:
- input[type="month"]
- input[type="week"]
- input[type="datetime"]
- input[type="datetime-local"]
-rw-r--r-- | actionpack/CHANGELOG.md | 3 | ||||
-rw-r--r-- | actionpack/lib/action_view/helpers/form_helper.rb | 68 | ||||
-rw-r--r-- | actionpack/lib/action_view/helpers/form_tag_helper.rb | 44 | ||||
-rw-r--r-- | actionpack/lib/action_view/helpers/tags.rb | 4 | ||||
-rw-r--r-- | actionpack/lib/action_view/helpers/tags/datetime_field.rb | 22 | ||||
-rw-r--r-- | actionpack/lib/action_view/helpers/tags/datetime_local_field.rb | 23 | ||||
-rw-r--r-- | actionpack/lib/action_view/helpers/tags/month_field.rb | 22 | ||||
-rw-r--r-- | actionpack/lib/action_view/helpers/tags/week_field.rb | 22 | ||||
-rw-r--r-- | actionpack/test/template/form_helper_test.rb | 140 | ||||
-rw-r--r-- | actionpack/test/template/form_tag_helper_test.rb | 20 | ||||
-rw-r--r-- | guides/source/form_helpers.textile | 12 |
11 files changed, 378 insertions, 2 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index d6fcae4571..920858d8c0 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,5 +1,8 @@ ## Rails 4.0.0 (unreleased) ## +* Add `week_field`, `week_field_tag`, `month_field`, `month_field_tag`, `datetime_local_field`, + `datetime_local_field_tag`, `datetime_field` and `datetime_field_tag` helpers. *Carlos Galdino* + * Add `color_field` and `color_field_tag` helpers. *Carlos Galdino* * `assert_generates`, `assert_recognizes`, and `assert_routing` all raise diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index c5a9057698..ad8885b708 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -1016,6 +1016,74 @@ module ActionView Tags::TimeField.new(object_name, method, self, options).render end + # Returns a text_field of type "datetime". + # + # datetime_field("user", "born_on") + # # => <input id="user_born_on" name="user[born_on]" type="datetime" /> + # + # The default value is generated by trying to call +strftime+ with "%Y-%m-%dT%T.%L%z" + # on the object's value, which makes it behave as expected for instances + # of DateTime and ActiveSupport::TimeWithZone. + # + # @user.born_on = Date.new(1984, 1, 12) + # datetime_field("user", "born_on") + # # => <input id="user_born_on" name="user[born_on]" type="datetime" value="1984-01-12T00:00:00.000+0000" /> + # + def datetime_field(object_name, method, options = {}) + Tags::DatetimeField.new(object_name, method, self, options).render + end + + # Returns a text_field of type "datetime-local". + # + # datetime_local_field("user", "born_on") + # # => <input id="user_born_on" name="user[born_on]" type="datetime-local" /> + # + # The default value is generated by trying to call +strftime+ with "%Y-%m-%dT%T" + # on the object's value, which makes it behave as expected for instances + # of DateTime and ActiveSupport::TimeWithZone. + # + # @user.born_on = Date.new(1984, 1, 12) + # datetime_local_field("user", "born_on") + # # => <input id="user_born_on" name="user[born_on]" type="datetime-local" value="1984-01-12T00:00:00" /> + # + def datetime_local_field(object_name, method, options = {}) + Tags::DatetimeLocalField.new(object_name, method, self, options).render + end + + # Returns a text_field of type "month". + # + # month_field("user", "born_on") + # # => <input id="user_born_on" name="user[born_on]" type="month" /> + # + # The default value is generated by trying to call +strftime+ with "%Y-%m" + # on the object's value, which makes it behave as expected for instances + # of DateTime and ActiveSupport::TimeWithZone. + # + # @user.born_on = Date.new(1984, 1, 27) + # month_field("user", "born_on") + # # => <input id="user_born_on" name="user[born_on]" type="date" value="1984-01" /> + # + def month_field(object_name, method, options = {}) + Tags::MonthField.new(object_name, method, self, options).render + end + + # Returns a text_field of type "week". + # + # week_field("user", "born_on") + # # => <input id="user_born_on" name="user[born_on]" type="week" /> + # + # The default value is generated by trying to call +strftime+ with "%Y-W%W" + # on the object's value, which makes it behave as expected for instances + # of DateTime and ActiveSupport::TimeWithZone. + # + # @user.born_on = Date.new(1984, 5, 12) + # week_field("user", "born_on") + # # => <input id="user_born_on" name="user[born_on]" type="date" value="1984-W19" /> + # + def week_field(object_name, method, options = {}) + Tags::WeekField.new(object_name, method, self, options).render + end + # Returns a text_field of type "url". # # url_field("user", "homepage") diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index 7da67cd2f9..1a0019a48c 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -568,6 +568,50 @@ module ActionView text_field_tag(name, value, options.stringify_keys.update("type" => "time")) end + # Creates a text field of type "datetime". + # + # === Options + # * <tt>:min</tt> - The minimum acceptable value. + # * <tt>:max</tt> - The maximum acceptable value. + # * <tt>:step</tt> - The acceptable value granularity. + # * Otherwise accepts the same options as text_field_tag. + def datetime_field_tag(name, value = nil, options = {}) + text_field_tag(name, value, options.stringify_keys.update("type" => "datetime")) + end + + # Creates a text field of type "datetime-local". + # + # === Options + # * <tt>:min</tt> - The minimum acceptable value. + # * <tt>:max</tt> - The maximum acceptable value. + # * <tt>:step</tt> - The acceptable value granularity. + # * Otherwise accepts the same options as text_field_tag. + def datetime_local_field_tag(name, value = nil, options = {}) + text_field_tag(name, value, options.stringify_keys.update("type" => "datetime-local")) + end + + # Creates a text field of type "month". + # + # === Options + # * <tt>:min</tt> - The minimum acceptable value. + # * <tt>:max</tt> - The maximum acceptable value. + # * <tt>:step</tt> - The acceptable value granularity. + # * Otherwise accepts the same options as text_field_tag. + def month_field_tag(name, value = nil, options = {}) + text_field_tag(name, value, options.stringify_keys.update("type" => "month")) + end + + # Creates a text field of type "week". + # + # === Options + # * <tt>:min</tt> - The minimum acceptable value. + # * <tt>:max</tt> - The maximum acceptable value. + # * <tt>:step</tt> - The acceptable value granularity. + # * Otherwise accepts the same options as text_field_tag. + def week_field_tag(name, value = nil, options = {}) + text_field_tag(name, value, options.stringify_keys.update("type" => "week")) + end + # Creates a text field of type "url". # # ==== Options diff --git a/actionpack/lib/action_view/helpers/tags.rb b/actionpack/lib/action_view/helpers/tags.rb index c56165c769..a05e16979a 100644 --- a/actionpack/lib/action_view/helpers/tags.rb +++ b/actionpack/lib/action_view/helpers/tags.rb @@ -11,12 +11,15 @@ module ActionView autoload :ColorField autoload :DateField autoload :DateSelect + autoload :DatetimeField + autoload :DatetimeLocalField autoload :DatetimeSelect autoload :EmailField autoload :FileField autoload :GroupedCollectionSelect autoload :HiddenField autoload :Label + autoload :MonthField autoload :NumberField autoload :PasswordField autoload :RadioButton @@ -30,6 +33,7 @@ module ActionView autoload :TimeSelect autoload :TimeZoneSelect autoload :UrlField + autoload :WeekField end end end diff --git a/actionpack/lib/action_view/helpers/tags/datetime_field.rb b/actionpack/lib/action_view/helpers/tags/datetime_field.rb new file mode 100644 index 0000000000..11d58744fd --- /dev/null +++ b/actionpack/lib/action_view/helpers/tags/datetime_field.rb @@ -0,0 +1,22 @@ +module ActionView + module Helpers + module Tags + class DatetimeField < TextField #:nodoc: + def render + options = @options.stringify_keys + options["value"] = @options.fetch("value") { format_global_date_time_string(value(object)) } + options["min"] = format_global_date_time_string(options["min"]) + options["max"] = format_global_date_time_string(options["max"]) + @options = options + super + end + + private + + def format_global_date_time_string(value) + value.try(:strftime, "%Y-%m-%dT%T.%L%z") + end + end + end + end +end diff --git a/actionpack/lib/action_view/helpers/tags/datetime_local_field.rb b/actionpack/lib/action_view/helpers/tags/datetime_local_field.rb new file mode 100644 index 0000000000..7593a3c733 --- /dev/null +++ b/actionpack/lib/action_view/helpers/tags/datetime_local_field.rb @@ -0,0 +1,23 @@ +module ActionView + module Helpers + module Tags + class DatetimeLocalField < TextField #:nodoc: + def render + options = @options.stringify_keys + options["type"] = "datetime-local" + options["value"] = @options.fetch("value") { format_local_date_time_string(value(object)) } + options["min"] = format_local_date_time_string(options["min"]) + options["max"] = format_local_date_time_string(options["max"]) + @options = options + super + end + + private + + def format_local_date_time_string(value) + value.try(:strftime, "%Y-%m-%dT%T") + end + end + end + end +end diff --git a/actionpack/lib/action_view/helpers/tags/month_field.rb b/actionpack/lib/action_view/helpers/tags/month_field.rb new file mode 100644 index 0000000000..56bd85a90b --- /dev/null +++ b/actionpack/lib/action_view/helpers/tags/month_field.rb @@ -0,0 +1,22 @@ +module ActionView + module Helpers + module Tags + class MonthField < TextField #:nodoc: + def render + options = @options.stringify_keys + options["value"] = @options.fetch("value") { format_month_string(value(object)) } + options["min"] = format_month_string(options["min"]) + options["max"] = format_month_string(options["max"]) + @options = options + super + end + + private + + def format_month_string(value) + value.try(:strftime, "%Y-%m") + end + end + end + end +end diff --git a/actionpack/lib/action_view/helpers/tags/week_field.rb b/actionpack/lib/action_view/helpers/tags/week_field.rb new file mode 100644 index 0000000000..a1265d9928 --- /dev/null +++ b/actionpack/lib/action_view/helpers/tags/week_field.rb @@ -0,0 +1,22 @@ +module ActionView + module Helpers + module Tags + class WeekField < TextField #:nodoc: + def render + options = @options.stringify_keys + options["value"] = @options.fetch("value") { format_week_string(value(object)) } + options["min"] = format_week_string(options["min"]) + options["max"] = format_week_string(options["max"]) + @options = options + super + end + + private + + def format_week_string(value) + value.try(:strftime, "%Y-W%W") + end + end + end + end +end diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index 76a18fa76a..97c8025db3 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -685,6 +685,146 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal(expected, time_field("post", "written_on")) end + def test_datetime_field + expected = %{<input id="post_written_on" name="post[written_on]" type="datetime" value="2004-06-15T00:00:00.000+0000" />} + assert_dom_equal(expected, datetime_field("post", "written_on")) + end + + def test_datetime_field_with_datetime_value + expected = %{<input id="post_written_on" name="post[written_on]" type="datetime" value="2004-06-15T01:02:03.000+0000" />} + @post.written_on = DateTime.new(2004, 6, 15, 1, 2, 3) + assert_dom_equal(expected, datetime_field("post", "written_on")) + end + + def test_datetime_field_with_extra_attrs + expected = %{<input id="post_written_on" step="60" max="2010-08-15T10:25:00.000+0000" min="2000-06-15T20:45:30.000+0000" name="post[written_on]" type="datetime" value="2004-06-15T01:02:03.000+0000" />} + @post.written_on = DateTime.new(2004, 6, 15, 1, 2, 3) + min_value = DateTime.new(2000, 6, 15, 20, 45, 30) + max_value = DateTime.new(2010, 8, 15, 10, 25, 00) + step = 60 + assert_dom_equal(expected, datetime_field("post", "written_on", :min => min_value, :max => max_value, :step => step)) + end + + def test_datetime_field_with_timewithzone_value + previous_time_zone, Time.zone = Time.zone, 'UTC' + expected = %{<input id="post_written_on" name="post[written_on]" type="datetime" value="2004-06-15T15:30:45.000+0000" />} + @post.written_on = Time.zone.parse('2004-06-15 15:30:45') + assert_dom_equal(expected, datetime_field("post", "written_on")) + ensure + Time.zone = previous_time_zone + end + + def test_datetime_field_with_nil_value + expected = %{<input id="post_written_on" name="post[written_on]" type="datetime" />} + @post.written_on = nil + assert_dom_equal(expected, datetime_field("post", "written_on")) + end + + def test_datetime_local_field + expected = %{<input id="post_written_on" name="post[written_on]" type="datetime-local" value="2004-06-15T00:00:00" />} + assert_dom_equal(expected, datetime_local_field("post", "written_on")) + end + + def test_datetime_local_field_with_datetime_value + expected = %{<input id="post_written_on" name="post[written_on]" type="datetime-local" value="2004-06-15T01:02:03" />} + @post.written_on = DateTime.new(2004, 6, 15, 1, 2, 3) + assert_dom_equal(expected, datetime_local_field("post", "written_on")) + end + + def test_datetime_local_field_with_extra_attrs + expected = %{<input id="post_written_on" step="60" max="2010-08-15T10:25:00" min="2000-06-15T20:45:30" name="post[written_on]" type="datetime-local" value="2004-06-15T01:02:03" />} + @post.written_on = DateTime.new(2004, 6, 15, 1, 2, 3) + min_value = DateTime.new(2000, 6, 15, 20, 45, 30) + max_value = DateTime.new(2010, 8, 15, 10, 25, 00) + step = 60 + assert_dom_equal(expected, datetime_local_field("post", "written_on", :min => min_value, :max => max_value, :step => step)) + end + + def test_datetime_local_field_with_timewithzone_value + previous_time_zone, Time.zone = Time.zone, 'UTC' + expected = %{<input id="post_written_on" name="post[written_on]" type="datetime-local" value="2004-06-15T15:30:45" />} + @post.written_on = Time.zone.parse('2004-06-15 15:30:45') + assert_dom_equal(expected, datetime_local_field("post", "written_on")) + ensure + Time.zone = previous_time_zone + end + + def test_datetime_local_field_with_nil_value + expected = %{<input id="post_written_on" name="post[written_on]" type="datetime-local" />} + @post.written_on = nil + assert_dom_equal(expected, datetime_local_field("post", "written_on")) + end + + def test_month_field + expected = %{<input id="post_written_on" name="post[written_on]" type="month" value="2004-06" />} + assert_dom_equal(expected, month_field("post", "written_on")) + end + + def test_month_field_with_nil_value + expected = %{<input id="post_written_on" name="post[written_on]" type="month" />} + @post.written_on = nil + assert_dom_equal(expected, month_field("post", "written_on")) + end + + def test_month_field_with_datetime_value + expected = %{<input id="post_written_on" name="post[written_on]" type="month" value="2004-06" />} + @post.written_on = DateTime.new(2004, 6, 15, 1, 2, 3) + assert_dom_equal(expected, month_field("post", "written_on")) + end + + def test_month_field_with_extra_attrs + expected = %{<input id="post_written_on" step="2" max="2010-12" min="2000-02" name="post[written_on]" type="month" value="2004-06" />} + @post.written_on = DateTime.new(2004, 6, 15, 1, 2, 3) + min_value = DateTime.new(2000, 2, 13) + max_value = DateTime.new(2010, 12, 23) + step = 2 + assert_dom_equal(expected, month_field("post", "written_on", :min => min_value, :max => max_value, :step => step)) + end + + def test_month_field_with_timewithzone_value + previous_time_zone, Time.zone = Time.zone, 'UTC' + expected = %{<input id="post_written_on" name="post[written_on]" type="month" value="2004-06" />} + @post.written_on = Time.zone.parse('2004-06-15 15:30:45') + assert_dom_equal(expected, month_field("post", "written_on")) + ensure + Time.zone = previous_time_zone + end + + def test_week_field + expected = %{<input id="post_written_on" name="post[written_on]" type="week" value="2004-W24" />} + assert_dom_equal(expected, week_field("post", "written_on")) + end + + def test_week_field_with_nil_value + expected = %{<input id="post_written_on" name="post[written_on]" type="week" />} + @post.written_on = nil + assert_dom_equal(expected, week_field("post", "written_on")) + end + + def test_week_field_with_datetime_value + expected = %{<input id="post_written_on" name="post[written_on]" type="week" value="2004-W24" />} + @post.written_on = DateTime.new(2004, 6, 15, 1, 2, 3) + assert_dom_equal(expected, week_field("post", "written_on")) + end + + def test_week_field_with_extra_attrs + expected = %{<input id="post_written_on" step="2" max="2010-W51" min="2000-W06" name="post[written_on]" type="week" value="2004-W24" />} + @post.written_on = DateTime.new(2004, 6, 15, 1, 2, 3) + min_value = DateTime.new(2000, 2, 13) + max_value = DateTime.new(2010, 12, 23) + step = 2 + assert_dom_equal(expected, week_field("post", "written_on", :min => min_value, :max => max_value, :step => step)) + end + + def test_week_field_with_timewithzone_value + previous_time_zone, Time.zone = Time.zone, 'UTC' + expected = %{<input id="post_written_on" name="post[written_on]" type="week" value="2004-W24" />} + @post.written_on = Time.zone.parse('2004-06-15 15:30:45') + assert_dom_equal(expected, week_field("post", "written_on")) + ensure + Time.zone = previous_time_zone + end + def test_url_field expected = %{<input id="user_homepage" name="user[homepage]" type="url" />} assert_dom_equal(expected, url_field("user", "homepage")) diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb index 195adce180..5d19e3274d 100644 --- a/actionpack/test/template/form_tag_helper_test.rb +++ b/actionpack/test/template/form_tag_helper_test.rb @@ -469,6 +469,26 @@ class FormTagHelperTest < ActionView::TestCase assert_dom_equal(expected, time_field_tag("cell")) end + def test_datetime_field_tag + expected = %{<input id="appointment" name="appointment" type="datetime" />} + assert_dom_equal(expected, datetime_field_tag("appointment")) + end + + def test_datetime_local_field_tag + expected = %{<input id="appointment" name="appointment" type="datetime-local" />} + assert_dom_equal(expected, datetime_local_field_tag("appointment")) + end + + def test_month_field_tag + expected = %{<input id="birthday" name="birthday" type="month" />} + assert_dom_equal(expected, month_field_tag("birthday")) + end + + def test_week_field_tag + expected = %{<input id="birthday" name="birthday" type="week" />} + assert_dom_equal(expected, week_field_tag("birthday")) + end + def test_url_field_tag expected = %{<input id="homepage" name="homepage" type="url" />} assert_dom_equal(expected, url_field_tag("homepage")) diff --git a/guides/source/form_helpers.textile b/guides/source/form_helpers.textile index 11d48551c4..8106de6f9d 100644 --- a/guides/source/form_helpers.textile +++ b/guides/source/form_helpers.textile @@ -150,7 +150,7 @@ NOTE: Always use labels for checkbox and radio buttons. They associate text with h4. Other Helpers of Interest -Other form controls worth mentioning are textareas, password fields, hidden fields, search fields, telephone fields, date fields, time fields, color fields, URL fields and email fields: +Other form controls worth mentioning are textareas, password fields, hidden fields, search fields, telephone fields, date fields, time fields, color fields, datetime fields, datetime-local fields, month fields, week fields, URL fields and email fields: <erb> <%= text_area_tag(:message, "Hi, nice site", :size => "24x6") %> @@ -159,6 +159,10 @@ Other form controls worth mentioning are textareas, password fields, hidden fiel <%= search_field(:user, :name) %> <%= telephone_field(:user, :phone) %> <%= date_field(:user, :born_on) %> +<%= datetime_field(:user, :meeting_time) %> +<%= datetime_local_field(:user, :graduation_day) %> +<%= month_field(:user, :birthday_month) %> +<%= week_field(:user, :birthday_week) %> <%= url_field(:user, :homepage) %> <%= email_field(:user, :address) %> <%= color_field(:user, :favorite_color) %> @@ -174,6 +178,10 @@ Output: <input id="user_name" name="user[name]" type="search" /> <input id="user_phone" name="user[phone]" type="tel" /> <input id="user_born_on" name="user[born_on]" type="date" /> +<input id="user_meeting_time" name="user[meeting_time]" type="datetime" /> +<input id="user_graduation_day" name="user[graduation_day]" type="datetime-local" /> +<input id="user_birthday_month" name="user[birthday_month]" type="month" /> +<input id="user_birthday_week" name="user[birthday_week]" type="week" /> <input id="user_homepage" name="user[homepage]" type="url" /> <input id="user_address" name="user[address]" type="email" /> <input id="user_favorite_color" name="user[favorite_color]" type="color" value="#000000" /> @@ -182,7 +190,7 @@ Output: Hidden inputs are not shown to the user but instead hold data like any textual input. Values inside them can be changed with JavaScript. -IMPORTANT: The search, telephone, date, time, color, URL, and email inputs are HTML5 controls. If you require your app to have a consistent experience in older browsers, you will need an HTML5 polyfill (provided by CSS and/or JavaScript). There is definitely "no shortage of solutions for this":https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-Browser-Polyfills, although a couple of popular tools at the moment are "Modernizr":http://www.modernizr.com/ and "yepnope":http://yepnopejs.com/, which provide a simple way to add functionality based on the presence of detected HTML5 features. +IMPORTANT: The search, telephone, date, time, color, datetime, datetime-local, month, week, URL, and email inputs are HTML5 controls. If you require your app to have a consistent experience in older browsers, you will need an HTML5 polyfill (provided by CSS and/or JavaScript). There is definitely "no shortage of solutions for this":https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-Browser-Polyfills, although a couple of popular tools at the moment are "Modernizr":http://www.modernizr.com/ and "yepnope":http://yepnopejs.com/, which provide a simple way to add functionality based on the presence of detected HTML5 features. TIP: If you're using password input fields (for any purpose), you might want to configure your application to prevent those parameters from being logged. You can learn about this in the "Security Guide":security.html#logging. |