4
4
# additional helpers like fieldset and hint.
5
5
# https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html
6
6
class UswdsFormBuilder < ActionView ::Helpers ::FormBuilder
7
+ standard_helpers = %i[ email_field file_field password_field text_area text_field ]
8
+
7
9
def initialize ( *args )
8
10
super
9
11
self . options [ :html ] ||= { }
@@ -19,25 +21,33 @@ def initialize(*args)
19
21
#
20
22
# Example usage:
21
23
# <%= f.text_field :foobar, { label: "Custom label text", hint: "Some hint text" } %>
22
- %i[ email_field file_field password_field text_area text_field ] . each do |field_type |
24
+ standard_helpers . each do |field_type |
23
25
define_method ( field_type ) do |attribute , options = { } |
24
26
classes = us_class_for_field_type ( field_type , options [ :width ] )
25
27
classes += " usa-input--error" if has_error? ( attribute )
26
-
27
- options [ :class ] ||= ""
28
- options [ :class ] . prepend ( "#{ classes } " )
28
+ append_to_option ( options , :class , " #{ classes } " )
29
29
30
30
label_text = options . delete ( :label )
31
+ label_class = options . delete ( :label_class ) || ""
32
+
33
+ label_options = options . except ( :width , :class , :id ) . merge ( {
34
+ class : label_class ,
35
+ for : options [ :id ]
36
+ } )
37
+ field_options = options . except ( :label , :hint , :large_label , :label_class )
31
38
32
- us_form_group ( attribute : attribute ) do
33
- us_text_field_label ( attribute , label_text , options ) + super ( attribute , options )
39
+ if options [ :hint ]
40
+ field_options [ :aria_describedby ] = hint_id ( attribute )
41
+ end
42
+
43
+ form_group ( attribute , options [ :group_options ] || { } ) do
44
+ us_text_field_label ( attribute , label_text , label_options ) + super ( attribute , field_options )
34
45
end
35
46
end
36
47
end
37
48
38
49
def check_box ( attribute , options = { } , *args )
39
- options [ :class ] ||= ""
40
- options [ :class ] . prepend ( us_class_for_field_type ( :check_box ) )
50
+ append_to_option ( options , :class , " #{ us_class_for_field_type ( :check_box ) } " )
41
51
42
52
label_text = options . delete ( :label )
43
53
@@ -47,8 +57,7 @@ def check_box(attribute, options = {}, *args)
47
57
end
48
58
49
59
def radio_button ( attribute , tag_value , options = { } )
50
- options [ :class ] ||= ""
51
- options [ :class ] . prepend ( us_class_for_field_type ( :radio_button ) )
60
+ append_to_option ( options , :class , " #{ us_class_for_field_type ( :radio_button ) } " )
52
61
53
62
label_text = options . delete ( :label )
54
63
label_options = { for : field_id ( attribute , tag_value ) } . merge ( options )
@@ -59,21 +68,21 @@ def radio_button(attribute, tag_value, options = {})
59
68
end
60
69
61
70
def select ( attribute , choices , options = { } , html_options = { } )
62
- classes = "usa-select"
63
-
64
- html_options [ :class ] ||= ""
65
- html_options [ :class ] . prepend ( "#{ classes } " )
71
+ append_to_option ( html_options , :class , " usa-select" )
66
72
67
73
label_text = options . delete ( :label )
68
74
69
- us_form_group ( attribute : attribute ) do
75
+ form_group ( attribute ) do
70
76
us_text_field_label ( attribute , label_text , options ) + super ( attribute , choices , options , html_options )
71
77
end
72
78
end
73
79
74
80
def submit ( value = nil , options = { } )
75
- options [ :class ] ||= ""
76
- options [ :class ] . prepend ( "usa-button " )
81
+ append_to_option ( options , :class , " usa-button" )
82
+
83
+ if options [ :big ]
84
+ append_to_option ( options , :class , " usa-button--big margin-y-6" )
85
+ end
77
86
78
87
super ( value , options )
79
88
end
@@ -92,16 +101,49 @@ def honeypot_field
92
101
# Custom helpers
93
102
########################################
94
103
104
+ def tax_id_field ( attribute , options = { } )
105
+ options [ :inputmode ] = "numeric"
106
+ options [ :placeholder ] = "_________"
107
+ options [ :width ] = "md"
108
+
109
+ append_to_option ( options , :class , " usa-masked" )
110
+ append_to_option ( options , :hint , @template . content_tag ( :p , I18n . t ( "us_form_with.tax_id_format" ) ) )
111
+
112
+ text_field ( attribute , options )
113
+ end
114
+
115
+ def date_picker ( attribute , options = { } )
116
+ raw_value = object . send ( attribute ) if object
117
+
118
+ append_to_option ( options , :hint , @template . content_tag ( :p , I18n . t ( "us_form_with.date_picker_format" ) ) )
119
+
120
+ group_options = options [ :group_options ] || { }
121
+ append_to_option ( group_options , :class , " usa-date-picker" )
122
+
123
+ if raw_value . is_a? ( Date )
124
+ append_to_option ( group_options , :"data-default-value" , raw_value . strftime ( "%Y-%m-%d" ) )
125
+ value = raw_value . strftime ( "%m/%d/%Y" ) if raw_value . is_a? ( Date )
126
+ end
127
+
128
+ text_field ( attribute , options . merge ( value : value , group_options : group_options ) )
129
+ end
130
+
95
131
def field_error ( attribute )
96
132
return unless has_error? ( attribute )
97
133
98
134
@template . content_tag ( :span , object . errors [ attribute ] . to_sentence , class : "usa-error-message" )
99
135
end
100
136
101
- def fieldset ( legend , attribute = nil , &block )
102
- us_form_group ( attribute : attribute ) do
137
+ def fieldset ( legend , options = { } , &block )
138
+ legend_classes = "usa-legend"
139
+
140
+ if options [ :large_legend ]
141
+ legend_classes += " usa-legend--large"
142
+ end
143
+
144
+ form_group ( options [ :attribute ] ) do
103
145
@template . content_tag ( :fieldset , class : "usa-fieldset" ) do
104
- @template . content_tag ( :legend , legend , class : "usa-legend" ) + @template . capture ( &block )
146
+ @template . content_tag ( :legend , legend , class : legend_classes ) + @template . capture ( &block )
105
147
end
106
148
end
107
149
end
@@ -121,6 +163,17 @@ def hint(text)
121
163
@template . content_tag ( :div , @template . raw ( text ) , class : "usa-hint" )
122
164
end
123
165
166
+ def form_group ( attribute = nil , options = { } , &block )
167
+ append_to_option ( options , :class , " usa-form-group" )
168
+ children = @template . capture ( &block )
169
+
170
+ if options [ :show_error ] or ( attribute and has_error? ( attribute ) )
171
+ append_to_option ( options , :class , " usa-form-group--error" )
172
+ end
173
+
174
+ @template . content_tag ( :div , children , options )
175
+ end
176
+
124
177
def yes_no ( attribute , options = { } )
125
178
yes_options = options [ :yes_options ] || { }
126
179
no_options = options [ :no_options ] || { }
@@ -132,7 +185,7 @@ def yes_no(attribute, options = {})
132
185
@template . capture do
133
186
# Hidden field included for same reason as radio button collections (https://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-collection_radio_buttons)
134
187
hidden_field ( attribute , value : "" ) +
135
- fieldset ( options [ :legend ] || human_name ( attribute ) , attribute ) do
188
+ fieldset ( options [ :legend ] || human_name ( attribute ) , { attribute : attribute } ) do
136
189
buttons =
137
190
radio_button ( attribute , true , yes_options ) +
138
191
radio_button ( attribute , false , no_options )
@@ -147,6 +200,16 @@ def yes_no(attribute, options = {})
147
200
end
148
201
149
202
private
203
+ def append_to_option ( options , key , value )
204
+ current_value = options [ key ] || ""
205
+
206
+ if current_value . is_a? ( Proc )
207
+ options [ key ] = -> { current_value . call + value }
208
+ else
209
+ options [ key ] = current_value + value
210
+ end
211
+ end
212
+
150
213
def us_class_for_field_type ( field_type , width = nil )
151
214
case field_type
152
215
when :check_box
@@ -167,15 +230,33 @@ def us_class_for_field_type(field_type, width = nil)
167
230
168
231
# Render the label, hint text, and error message for a form field
169
232
def us_text_field_label ( attribute , text = nil , options = { } )
170
- hint_text = options . delete ( :hint )
233
+ hint_option = options . delete ( :hint )
234
+ classes = "usa-label"
235
+ for_attr = options [ :for ] || field_id ( attribute )
171
236
172
- if hint_text
173
- hint_id = "#{ attribute } _hint"
174
- options [ :aria_describedby ] = hint_id
175
- hint = @template . content_tag ( :div , @template . raw ( hint_text ) , id : hint_id , class : "usa-hint" )
237
+ if options [ :class ]
238
+ classes += " #{ options [ :class ] } "
239
+ end
240
+
241
+ unless text
242
+ text = human_name ( attribute )
176
243
end
177
244
178
- label ( attribute , text , { class : "usa-label" } ) + hint + field_error ( attribute )
245
+ if options [ :optional ]
246
+ text += @template . content_tag ( :span , " (#{ I18n . t ( 'us_form_with.optional' ) . downcase } )" , class : "usa-hint" )
247
+ end
248
+
249
+ if hint_option
250
+ if hint_option . is_a? ( Proc )
251
+ hint_content = @template . capture ( &hint_option )
252
+ else
253
+ hint_content = @template . raw ( hint_option )
254
+ end
255
+
256
+ hint = @template . content_tag ( :div , hint_content , id : hint_id ( attribute ) , class : "usa-hint" )
257
+ end
258
+
259
+ label ( attribute , @template . raw ( text ) , { class : classes , for : for_attr } ) + field_error ( attribute ) + hint
179
260
end
180
261
181
262
# Label for a checkbox or radio
@@ -192,11 +273,7 @@ def us_toggle_label(type, attribute, text = nil, options = {})
192
273
label ( attribute , label_text , options )
193
274
end
194
275
195
- def us_form_group ( attribute : nil , show_error : nil , &block )
196
- children = @template . capture ( &block )
197
- classes = "usa-form-group"
198
- classes += " usa-form-group--error" if show_error or ( attribute and has_error? ( attribute ) )
199
-
200
- @template . content_tag ( :div , children , class : classes )
276
+ def hint_id ( attribute )
277
+ "#{ attribute } _hint"
201
278
end
202
279
end
0 commit comments