form_props 0.0.1

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