form_props 0.0.1

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 (38) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +714 -0
  3. data/lib/form_props/action_view_extensions/form_helper.rb +120 -0
  4. data/lib/form_props/form_builder.rb +223 -0
  5. data/lib/form_props/form_options_helper.rb +158 -0
  6. data/lib/form_props/inputs/base.rb +164 -0
  7. data/lib/form_props/inputs/check_box.rb +69 -0
  8. data/lib/form_props/inputs/collection_check_boxes.rb +36 -0
  9. data/lib/form_props/inputs/collection_helpers.rb +53 -0
  10. data/lib/form_props/inputs/collection_radio_buttons.rb +35 -0
  11. data/lib/form_props/inputs/collection_select.rb +30 -0
  12. data/lib/form_props/inputs/color_field.rb +27 -0
  13. data/lib/form_props/inputs/date_field.rb +17 -0
  14. data/lib/form_props/inputs/datetime_field.rb +32 -0
  15. data/lib/form_props/inputs/datetime_local_field.rb +17 -0
  16. data/lib/form_props/inputs/email_field.rb +13 -0
  17. data/lib/form_props/inputs/file_field.rb +13 -0
  18. data/lib/form_props/inputs/grouped_collection_select.rb +31 -0
  19. data/lib/form_props/inputs/hidden_field.rb +16 -0
  20. data/lib/form_props/inputs/month_field.rb +17 -0
  21. data/lib/form_props/inputs/number_field.rb +21 -0
  22. data/lib/form_props/inputs/password_field.rb +18 -0
  23. data/lib/form_props/inputs/radio_button.rb +48 -0
  24. data/lib/form_props/inputs/range_field.rb +13 -0
  25. data/lib/form_props/inputs/search_field.rb +28 -0
  26. data/lib/form_props/inputs/select.rb +42 -0
  27. data/lib/form_props/inputs/submit.rb +26 -0
  28. data/lib/form_props/inputs/tel_field.rb +13 -0
  29. data/lib/form_props/inputs/text_area.rb +37 -0
  30. data/lib/form_props/inputs/text_field.rb +28 -0
  31. data/lib/form_props/inputs/time_field.rb +17 -0
  32. data/lib/form_props/inputs/time_zone_select.rb +22 -0
  33. data/lib/form_props/inputs/url_field.rb +13 -0
  34. data/lib/form_props/inputs/week_field.rb +17 -0
  35. data/lib/form_props/inputs/weekday_select.rb +28 -0
  36. data/lib/form_props/version.rb +5 -0
  37. data/lib/form_props.rb +45 -0
  38. metadata +120 -0
data/README.md ADDED
@@ -0,0 +1,714 @@
1
+ # Form Props
2
+
3
+ FormProps is a Rails form builder that outputs input attributes (in JSON) instead of
4
+ tags. Now you can enjoy the conviences of Rails helpers in other view libraries like
5
+ React, and React Native.
6
+
7
+ By separting attributes from tags, FormProps can offer greater flexbility than normal
8
+ Rails form builders; allowing designers to stay longer in HTML land and more easily
9
+ customize their form structure without needing to know Rails.
10
+
11
+ ## Caution
12
+
13
+ This project is in its early phases of development. Its interface, behavior,
14
+ and name are likely to change drastically before a major version release.
15
+
16
+ ## Installation
17
+
18
+ Add to your `Gemfile`
19
+
20
+ ```
21
+ gem "form_props"
22
+ ```
23
+
24
+ and `bundle install`
25
+
26
+ ## Usage
27
+ `form_props` is designed to be used in a [PropsTemplate] template (it can work with
28
+ [jbuilder](#jbuilder)). For example in your `new.json.props`:
29
+
30
+ ```ruby
31
+ json.some_form do
32
+ form_props(@post) do |f|
33
+ f.text :title
34
+ f.submit
35
+ end
36
+ end
37
+ ```
38
+
39
+ would output
40
+
41
+ ```
42
+ {
43
+ someForm: {
44
+ props: {
45
+ id: "create-post",
46
+ action: "/posts/123",
47
+ accept-charset: "UTF-8",
48
+ method: "post"
49
+ },
50
+ extras: {
51
+ method: {
52
+ name: "_method",
53
+ type: "hidden",
54
+ defaultValue: "patch",
55
+ autocomplete: "off"
56
+ },
57
+ utf8: {
58
+ name: "utf8",
59
+ type: "hidden",
60
+ defaultValue: "\u0026#x2713;",
61
+ autocomplete: "off"
62
+ }
63
+ csrf: {
64
+ name: "utf8",
65
+ type: "authenticity_token",
66
+ defaultValue: "SomeTOken!23$",
67
+ autocomplete: "off"
68
+ }
69
+ },
70
+ inputs: {
71
+ name: {type: "text", defaultValue: "hello"},
72
+ submit: {type: "submit", value: "Update a Post"}
73
+ }
74
+ }
75
+ }
76
+ ```
77
+
78
+ You can then proceed to use this output in React like so:
79
+
80
+ ```js
81
+ import React from 'react'
82
+
83
+ export default ({props, inputs, extras}) => {
84
+ <form {...props}>
85
+ {Object.values(extras).map((hiddenProps) => (<input {...hiddenProps} type="hidden"/>))}
86
+
87
+ <input {...inputs.name} type="text"/>
88
+ <label for={inputs.name.id}>Your Name</label>
89
+
90
+ <input {...inputs.submit} type="submit"/>
91
+ </form>
92
+ }
93
+ ```
94
+
95
+ ### Key format
96
+ By default, props_template automatically `camelize(:lower)` on all keys. All
97
+ documentation here reflects that default. You can change that [behavior](https://github.com/thoughtbot/props_template#change-key-format)
98
+ if you wish.
99
+
100
+ ## Flexibility
101
+ FormProps is only concerned about attributes, the designer can focus on tag
102
+ structure and stay longer in HTML land. For example, you can decide to nest an
103
+ input inside a label.
104
+
105
+ ```js
106
+ <label for={inputs.name.id}>
107
+ Your Name
108
+ <input {...inputs.name} type="text"/>
109
+ </label>
110
+ ```
111
+
112
+ or not
113
+
114
+ ```js
115
+ <label for={inputs.name.id}>Your Name</label>
116
+ <input {...inputs.name} />
117
+ ```
118
+
119
+ ## Custom Components
120
+
121
+ With `form_props` you can combine the comprehensiveness of Rails forms with
122
+ your prefered React components:
123
+
124
+ For example:
125
+
126
+ ```js
127
+ json.some_form do
128
+ form_props(@post) do |f|
129
+ f.time_zone_select(:time_zone)
130
+ ...
131
+ end
132
+ end
133
+ ```
134
+
135
+ Then use it the props your own components or a external component like
136
+ `react-select`:
137
+
138
+ ```js
139
+ import React from 'react'
140
+ import Select from 'react-select';
141
+
142
+ export default (({props, inputs, extras})) => {
143
+ return (
144
+ <form {...props}>
145
+ <Select
146
+ {...inputs.timeZone}
147
+ isMulti={inputs.timeZone.multiple}
148
+ />
149
+ </form>
150
+ )
151
+ }
152
+ ```
153
+
154
+ ## Error handling
155
+
156
+ FormProps doesn't handle form errors, but you can easily add this functionality:
157
+
158
+ ```ruby
159
+ json.someForm do
160
+ form_props(@post) do |f|
161
+ f.text_field :title
162
+ end
163
+
164
+ json.errors @post.errors.to_hash(true)
165
+ end
166
+ ```
167
+
168
+ then merge it later
169
+
170
+ ```js
171
+ <MyTextComponent {...someForm.inputs.title, error: ...someForm.errors.title}>
172
+ ```
173
+
174
+ ## form_props
175
+ `form_props` shares most of same arguments as [form_with]. The differences are
176
+
177
+ 1. `remote` and `local` options are removed.
178
+ 2. You can change the name of the value keys generated by the [form helpers](#form-helpers)
179
+ from `defaultValue` to `value`, by using `controlled: true`. For example:
180
+
181
+ ```ruby
182
+ json.some_form do
183
+ form_props(@post, controlled: true) do |f|
184
+ f.text_field :title
185
+ end
186
+ end
187
+ ```
188
+
189
+ By default, the `controlled` option is `false`.
190
+
191
+ ###
192
+
193
+ `props` Attributes that you can splat direclty into your `<form>` element.
194
+
195
+
196
+ `extras` contain hidden input attributes that are created by form_props
197
+ indirectly, for example, the `csrf` token. Its best to wrap this in a custom
198
+ component that does the following. An [Extra] component is available
199
+
200
+ ```js
201
+ Object.values(extras).map((hiddenProps) => (<input {...hiddenProps} type="hidden"/>))}
202
+ ```
203
+
204
+
205
+ ## Form Helpers
206
+
207
+ `form_props` provides its own version of the following Rails form helpers:
208
+
209
+ ```
210
+ check_box file_field submit
211
+ collection_check_boxes grouped_collection_select tel_field
212
+ collection_helpers hidden_field text_area
213
+ collection_radio_buttons month_field text_field
214
+ collection_select number_field time_field
215
+ color_field password_field time_zone_select
216
+ date_field radio_button url_field
217
+ datetime_field range_field week_field
218
+ datetime_local_field search_field weekday_select
219
+ email_field select
220
+ ```
221
+
222
+ `form_props` is a fork of `form_with`, and the accompanying form builder
223
+ inherits from `ActionView::Helpers::FormBuilder`.
224
+
225
+ Many of the helpers accept the same arguments and you can continue to rely on
226
+ [Rails Guides for form helpers] for guidance, but as the goal of `form_props`
227
+ is to focus on attributes instead of tags there are a few general differences
228
+ across all helpers that would beneficial to know:
229
+
230
+ 1. The form helper `f.label` do not exist. Helpers like the below that `yield`s
231
+ for label structure
232
+
233
+ ```
234
+ f.collection_radio_buttons(:active, [true, false], :to_s, :to_s) do |b|
235
+ b.label { b.radio_button + b.text }
236
+ end
237
+ ```
238
+
239
+ no longer takes in blocks to do so.
240
+
241
+ 2. `defaultValue`s are not escaped. Instead, we lean on PropsTemplate
242
+ to [escape] JSON and HTML entities.
243
+ 3. `defaultValue` will not appear as a key if no `value` was set.
244
+ 3. `data-disable-with` is removed on submit buttons.
245
+ 4. `data-remote` is removed from form props.
246
+ 5. For helpers selectively render hidden inputs, we passed the attribute to
247
+ 5. `f.select` helpers does not render `selected` on `options`, instead it follows
248
+ react caveats and renders on the input's `value`. For example:
249
+
250
+ ```js
251
+ {
252
+ "type": "select",
253
+ "name": "continent[countries]",
254
+ "id": "continent_countries",
255
+ "multiple": true,
256
+ "defaultValue": ["Africa", "Europe"],
257
+ "options": [
258
+ {"value": "Africa", "label": "Africa"},
259
+ {"value": "Europe", "label": "Europe"},
260
+ {"value": "America", "label": "America", "disabled": true}
261
+ ]
262
+ }
263
+ ```
264
+
265
+ ### Unsupported helpers
266
+ `form_props` does **not** support:
267
+
268
+ `label`. We encourage you to use the tag directly in combination with other
269
+ helpers. For example:
270
+
271
+ ```
272
+ <label for={inputs.name.id} />
273
+ ```
274
+
275
+ `rich_text_area`. We encourage you to use the `f.text_area` helper in
276
+ combination with Trix wrapped in React, or TinyMCE's react component.
277
+
278
+ `button`. We encourage you to use the tag directly.
279
+
280
+ `date_select`, `time_select`, `datetime_select`. We encourage you to use other
281
+ alternatives like `react-date-picker` in combination with other supported date
282
+ field helpers.
283
+
284
+ ## Text helpers
285
+
286
+ [text_field], [email_field], [tel_field], [file_field], [url_field],
287
+ [hidden_field], and the slight variations [password_field],
288
+ [search_field], [color_field] has the same arguments as their Rails
289
+ counterpart.
290
+
291
+ When used like so
292
+
293
+ ```ruby
294
+ form_props(model: @post) do |f|
295
+ f.text_field(:title)
296
+ end
297
+ ```
298
+
299
+ `inputs.title` would output
300
+
301
+ ```
302
+ {
303
+ "type": "text",
304
+ "defaultValue": "Hello World",
305
+ "name": "post[title]",
306
+ "id": "post_title"
307
+ }
308
+ ```
309
+
310
+ ## Date helpers
311
+ [date_field], [datetime_field], [datetime_local_field], [month_field],
312
+ [week_field] has the same arguments as their Rails counterparts.
313
+
314
+ When used like so
315
+
316
+ ```ruby
317
+ form_props(model: @post) do |f|
318
+ f.datetime_field(:created_at)
319
+ end
320
+ ```
321
+
322
+ `inputs.created_at` would output
323
+
324
+ ```json
325
+ {
326
+ "type": "datetime-local",
327
+ "defaultValue": "2004-06-15T01:02:03",
328
+ "name": "post[created_at]",
329
+ "id": "post_created_at"
330
+ }
331
+ ```
332
+
333
+ ## Number helpers
334
+ [number_field], [range_field] has the same arguments as their Rails counterparts.
335
+
336
+ When used like so
337
+
338
+ ```ruby
339
+ @post.favs = 2
340
+
341
+ form_props(model: @post) do |f|
342
+ f.range_field(:favs, in: 1...10)
343
+ end
344
+ ```
345
+
346
+ `inputs.favs` would output
347
+
348
+ ```json
349
+ {
350
+ "type": "range",
351
+ "defaultValue": "2",
352
+ "name": "post[favs]",
353
+ "min": 1,
354
+ "max": 9,
355
+ "id": "post_favs"
356
+ }
357
+ ```
358
+
359
+ ## Checkbox helper
360
+ [check_box] has the same arguments its Rails counterpart.
361
+
362
+ The original Rails `check_box` helper renders an unchecked value in a
363
+ hidden input. While `form_props` doesn't generate the tags, the
364
+ `unchecked_value`, and `include_hidden` can be passed to a React component
365
+ to replicate that behavior. This repository has an example [CheckBox]
366
+ component used in its test that you can refer to.
367
+
368
+ When used like so:
369
+
370
+ ```ruby
371
+ @post.admin = "on"
372
+
373
+ form_props(model: @post) do |f|
374
+ f.check_box(:admin, {}, "on", "off")
375
+ end
376
+ ```
377
+
378
+ `inputs.admin` would output
379
+
380
+ ```json
381
+ {
382
+ "type": "checkbox",
383
+ "defaultValue": "on",
384
+ "uncheckedValue": "off",
385
+ "name": "post[admin]",
386
+ "id": "post_admin",
387
+ "includeHidden": true
388
+ }
389
+ ```
390
+
391
+ ## Radio helper
392
+ [radio_button] has the same arguments as its Rails counterpart. The radio button is unique
393
+
394
+ When used like so:
395
+
396
+ ```ruby
397
+ @post.admin = false
398
+
399
+ form_props(model: @post) do |f|
400
+ f.radio_button(:admin, true)
401
+ f.radio_button(:admin, false)
402
+ end
403
+ ```
404
+
405
+ The keys on `inputs` are a combination of the name and value. So `inputs.adminTrue`
406
+ would output:
407
+
408
+ ```json
409
+ {
410
+ "type": "radio",
411
+ "defaultValue": "true",
412
+ "name": "post[admin]",
413
+ "id": "post_admin_true"
414
+ }
415
+ ```
416
+
417
+ and `inputs.adminFalse` would output
418
+
419
+ ```json
420
+ {
421
+ "type": "radio",
422
+ "defaultValue": "false",
423
+ "name": "post[admin]",
424
+ "id": "post_admin_false",
425
+ "checked": true
426
+ }
427
+ ```
428
+
429
+ ## Select helpers
430
+ [select], [weekday_select], [time_zone_select] mostly has the same arguments
431
+ as its Rails counterpart. They key difference is that choices for select cannot be a string:
432
+
433
+ ```ruby
434
+ # BAD!!!
435
+
436
+ form_props(model: @post) do |f|
437
+ f.select(:category, "<option><option/>", multiple: false)
438
+ end
439
+
440
+ # Good
441
+
442
+ form_props(model: @post) do |f|
443
+ f.select(:category, [], multiple: false)
444
+ end
445
+ ```
446
+
447
+ When used like so
448
+
449
+ ```ruby
450
+ @post.category = "lifestyle"
451
+
452
+ form_props(model: @post) do |f|
453
+ f.select(:category, ["lifestyle", "programming", "spiritual"], {selected: "", disabled: "", prompt: "Choose one"}, {required: true})
454
+ end
455
+
456
+ ```
457
+
458
+ `inputs.category` would output
459
+
460
+ ```
461
+ {
462
+ "type": "select",
463
+ "required": true,
464
+ "name": "post[category]",
465
+ "id": "post_category",
466
+ "defaultValue":"lifestyle",
467
+ "options": [
468
+ {"disabled": true, "value": "", "label": "Choose one"},
469
+ {"value": "lifestyle", "label": "lifestyle"},
470
+ {"value": "programming", "label": "programming"},
471
+ {"value": "spiritual", "label": "spiritual"}
472
+ ]
473
+ }
474
+ ```
475
+
476
+ Of note:
477
+ 1. Notice that we follow react caveats and put `selected` values on `defaultValue`. This rule
478
+ does not apply to the `disabled` attribute on option.
479
+ 2. When `multiple: true`, `defaultValue` is an array of values.
480
+ 3. The key, `defaultValue` is only set if the value is in options. For example:
481
+
482
+ ```
483
+ form_props(model: @post) do |f|
484
+ f.select(:category, [])
485
+ end
486
+ ```
487
+
488
+ would output in `inputs.category`:
489
+
490
+ ```
491
+ {
492
+ "type": "select",
493
+ "name": "post[category]",
494
+ "id": "post_category",
495
+ "options": []
496
+ }
497
+ ```
498
+
499
+ As the `select` helper renders nested options and `includeHidden`, a custom
500
+ component is required to correctly render the tag structure. A reference
501
+ [Select component] implementation is availble that is used in our tests.
502
+
503
+ The `select` helper can also output a grouped collection.
504
+
505
+ ```ruby
506
+ @post = Post.new
507
+ countries_by_continent = [
508
+ ["<Africa>", [["<South Africa>", "<sa>"], ["Somalia", "so"]]],
509
+ ["Europe", [["Denmark", "dk"], ["Ireland", "ie"]]]
510
+ ]
511
+
512
+ form_props(model: @post) do |f|
513
+ f.select(:category, countries_by_continent)
514
+ end
515
+ ```
516
+
517
+ `inputs.category` would output:
518
+
519
+ ```json
520
+ {
521
+ "type": "select",
522
+ "name": "post[category]",
523
+ "id": "post_category",
524
+ "options": [
525
+ {
526
+ "label": "<Africa>", "options": [
527
+ {"value": "<sa>", "label": "<South Africa>"},
528
+ {"value": "so", "label": "Somalia"}
529
+ ]
530
+ },
531
+ {
532
+ "label": "Europe", "options": [
533
+ {"value": "dk", "label": "Denmark"},
534
+ {"value": "ie", "label": "Ireland"}
535
+ ]
536
+ }
537
+ ]
538
+ }
539
+ ```
540
+
541
+
542
+ ## Group collection select
543
+ [group_collection_select] has the same arguments its Rails counterpart.
544
+
545
+ Like `select`, you'll need combine this with a custom `Select` component. An
546
+ example [Select component] is available.
547
+
548
+ When used like so:
549
+
550
+ ```ruby
551
+
552
+ @post = Post.new
553
+ @post.country = "dk"
554
+ label_proc = proc { |c| c.id }
555
+
556
+ continents = [
557
+ Continent.new("<Africa>", [Country.new("<sa>", "<South Africa>"), Country.new("so", "Somalia")]),
558
+ Continent.new("Europe", [Country.new("dk", "Denmark"), Country.new("ie", "Ireland")])
559
+ ]
560
+
561
+ form_props(model: @post) do |f|
562
+ f.grouped_collection_select(
563
+ :country, continents, "countries", label_proc, "country_id", "country_name"
564
+ )
565
+ end
566
+ ```
567
+
568
+ `inputs.country` would output
569
+
570
+ ```json
571
+ {
572
+ "name": "post[country]",
573
+ "id": "post_country",
574
+ "type": "select",
575
+ "defaultValue": "dk",
576
+ "options": [
577
+ {
578
+ "label":"<Africa>",
579
+ "options": [
580
+ {"value": "<sa>", "label": "<South Africa>"},
581
+ {"value": "so", "label": "Somalia"}
582
+ ]
583
+ }, {
584
+ "label": "Europe",
585
+ "options": [
586
+ {"value": "dk", "label": "Denmark"},
587
+ {"value":"ie", "label": "Ireland"}
588
+ ]
589
+ }
590
+ ]
591
+ }
592
+ ```
593
+
594
+ ## Collection select
595
+ [collection_select], [collection_radio_buttons], and [collection_check_boxes]
596
+ has the same arguments its Rails counterpart, but their output differs slightly.
597
+
598
+
599
+ [collection_select] follows the same output as `f.select`. When used like so:
600
+
601
+ ```
602
+ dummy_posts = [
603
+ Post.new(1, "<Abe> went home", "<Abe>", "To a little house", "shh!"),
604
+ Post.new(2, "Babe went home", "Babe", "To a little house", "shh!"),
605
+ Post.new(3, "Cabe went home", "Cabe", "To a little house", "shh!")
606
+ ]
607
+
608
+
609
+ form_props(model: @post) do |f|
610
+ f.collection_select(:author_name, dummy_posts, "author_name", "author_name")
611
+ end
612
+ ```
613
+
614
+ `inputs.authorName` would output:
615
+
616
+ ```
617
+ {
618
+ "type": "select",
619
+ "name": "post[author_name]",
620
+ "id": "post_author_name",
621
+ "defaultValue": "Babe",
622
+ "options": [
623
+ {"value": "<Abe>", "label": "<Abe>"},
624
+ {"value": "Babe", "label": "Babe"},
625
+ {"value": "Cabe", "label": "Cabe"}
626
+ ]
627
+ }
628
+ ```
629
+
630
+ [collection_radio_buttons] and [collection_check_boxes] usage is the same with
631
+ their rails counterpart, and when used, would render:
632
+
633
+ ```
634
+ {
635
+ "collection": [
636
+ {"name":"user[other_category_ids][]","type": "checkbox", "defaultValue": "1", "uncheckedValue":"","id":"user_category_ids_1","label": "Category 1"},
637
+ {"name":"user[other_category_ids][]","type": "checkbox", "defaultValue": "2", "uncheckedValue":"","id":"user_category_ids_2","label": "Category 2"}
638
+ ],
639
+ "name": "user[other_category_ids][]",
640
+ "includeHidden": true
641
+ }
642
+ ```
643
+
644
+ Like select, you would need a custom component to render. An example
645
+ implementation for [CollectionCheckBoxes] and [CollectionRadioButtons] are
646
+ available.
647
+
648
+ ## jbuilder
649
+
650
+ form_props can work with jbuilder, but needs an extra call in the beginning of
651
+ your template to `FormProps.set` to inject `json`. For example.
652
+
653
+ ```ruby
654
+ FormProps.set(json, self)
655
+
656
+ json.data do
657
+ json.hello "world"
658
+
659
+ json.form do
660
+ form_props(model: User.new, url: "/") do |f|
661
+ f.text_field(:email)
662
+ f.submit
663
+ end
664
+ end
665
+ end
666
+ ```
667
+
668
+ [escape]: https://github.com/thoughtbot/props_template#escape-mode
669
+ [form_with]: https://api.rubyonrails.org/v7.0.4.2/classes/ActionView/Helpers/FormHelper.html#method-i-form_with
670
+ [Extra]: ./components/Extras.js
671
+ [CollectionCheckBoxes]: ./components/CollectionCheckBoxes.js
672
+ [CollectionRadioButtons]: ./components/CollectionRadioButtons.js
673
+ [Select Component]: ./components/Select.js
674
+ [select]: https://api.rubyonrails.org/v7.0.4.2/classes/ActionView/Helpers/FormBuilder.html#method-i-select
675
+ [CheckBox]: ./components/CheckBox.js
676
+ [PropsTemplate]: https://github.com/thoughtbot/props_template
677
+ [text_field]: https://api.rubyonrails.org/v7.0.4.2/classes/ActionView/Helpers/FormHelper.html#method-i-text_field
678
+ [tel_field]: https://api.rubyonrails.org/v7.0.4.2/classes/ActionView/Helpers/FormHelper.html#method-i-tel_field
679
+ [file_field]: https://api.rubyonrails.org/v7.0.4.2/classes/ActionView/Helpers/FormHelper.html#method-i-file_field
680
+ [week_field]: https://api.rubyonrails.org/v7.0.4.2/classes/ActionView/Helpers/FormHelper.html#method-i-week_field
681
+ [url_field]: https://api.rubyonrails.org/v7.0.4.2/classes/ActionView/Helpers/FormHelper.html#method-i-url_field
682
+ [telephone_field]: https://api.rubyonrails.org/v7.0.4.2/classes/ActionView/Helpers/FormHelper.html#method-i-telephone_field
683
+ [text_area]: https://api.rubyonrails.org/v7.0.4.2/classes/ActionView/Helpers/FormHelper.html#method-i-text_area
684
+ [text_field]: https://api.rubyonrails.org/v7.0.4.2/classes/ActionView/Helpers/FormHelper.html#method-i-text_field
685
+ [time_field]: https://api.rubyonrails.org/v7.0.4.2/classes/ActionView/Helpers/FormHelper.html#method-i-time_field
686
+ [search_field]: https://api.rubyonrails.org/v7.0.4.2/classes/ActionView/Helpers/FormHelper.html#method-i-search_field
687
+ [radio_button]: https://api.rubyonrails.org/v7.0.4.2/classes/ActionView/Helpers/FormHelper.html#method-i-radio_button
688
+ [range_field]: https://api.rubyonrails.org/v7.0.4.2/classes/ActionView/Helpers/FormHelper.html#method-i-range_field
689
+ [password_field]: https://api.rubyonrails.org/v7.0.4.2/classes/ActionView/Helpers/FormHelper.html#method-i-password_field
690
+ [phone_field]: https://api.rubyonrails.org/v7.0.4.2/classes/ActionView/Helpers/FormHelper.html#method-i-phone_field
691
+ [number_field]: https://api.rubyonrails.org/v7.0.4.2/classes/ActionView/Helpers/FormHelper.html#method-i-number_field
692
+ [month_field]: https://api.rubyonrails.org/v7.0.4.2/classes/ActionView/Helpers/FormHelper.html#method-i-month_field
693
+ [hidden_field]: https://api.rubyonrails.org/v7.0.4.2/classes/ActionView/Helpers/FormHelper.html#method-i-hidden_field
694
+ [fields]: https://api.rubyonrails.org/v7.0.4.2/classes/ActionView/Helpers/FormHelper.html#method-i-fields
695
+ [fields_for]: https://api.rubyonrails.org/v7.0.4.2/classes/ActionView/Helpers/FormHelper.html#method-i-fields_for
696
+ [field_field]: https://api.rubyonrails.org/v7.0.4.2/classes/ActionView/Helpers/FormHelper.html#method-i-file_field
697
+ [form_with]: https://api.rubyonrails.org/v7.0.4.2/classes/ActionView/Helpers/FormHelper.html#method-i-form_with
698
+ [email_field]: https://api.rubyonrails.org/v7.0.4.2/classes/ActionView/Helpers/FormHelper.html#method-i-email_field
699
+ [date_field]: https://api.rubyonrails.org/v7.0.4.2/classes/ActionView/Helpers/FormHelper.html#method-i-date_field
700
+ [datetime_field]: https://api.rubyonrails.org/v7.0.4.2/classes/ActionView/Helpers/FormHelper.html#method-i-datetime_field
701
+ [datetime_local_field]: https://api.rubyonrails.org/v7.0.4.2/classes/ActionView/Helpers/FormHelper.html#method-i-datetime_local_field
702
+ [check_box]: https://api.rubyonrails.org/v7.0.4.2/classes/ActionView/Helpers/FormHelper.html#method-i-check_box
703
+ [color_field]: https://api.rubyonrails.org/v7.0.4.2/classes/ActionView/Helpers/FormHelper.html#method-i-color_field
704
+ [grouped_collection_select]: https://api.rubyonrails.org/v7.0.4.2/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-grouped_collection_select
705
+ [collection_radio_buttons]: https://api.rubyonrails.org/v7.0.4.2/classes/ActionView/Helpers/FormBuilder.html#method-i-collection_radio_buttons
706
+ [collection_select]: https://api.rubyonrails.org/v7.0.4.2/classes/ActionView/Helpers/FormBuilder.html#method-i-collection_select
707
+ [collection_check_boxes]: https://api.rubyonrails.org/v7.0.4.2/classes/ActionView/Helpers/FormBuilder.html#method-i-collection_check_boxes
708
+ [weekday_select]: https://api.rubyonrails.org/v7.0.4.2/classes/ActionView/Helpers/FormBuilder.html#method-i-weekday_select
709
+ [group_collection_select]: https://api.rubyonrails.org/v7.0.4.2/classes/ActionView/Helpers/FormBuilder.html#method-i-grouped_collection_select
710
+
711
+ ## Special Thanks
712
+
713
+ Thanks to [bootstrap_form](https://github.com/bootstrap-ruby/bootstrap_form) documentation for inspiration.
714
+