view_component-form 0.1.2 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +31 -1
- data/README.md +215 -2
- data/app/components/view_component/form/base_component.rb +1 -4
- data/app/components/view_component/form/error_message_component.rb +24 -0
- data/app/components/view_component/form/field_component.rb +40 -0
- data/app/components/view_component/form/hint_component.rb +37 -0
- data/app/components/view_component/form/weekday_select_component.rb +38 -0
- data/lib/generators/vcf/builder/builder_generator.rb +0 -2
- data/lib/generators/vcf/builder/templates/builder.rb.erb +2 -0
- data/lib/view_component/form/builder.rb +31 -2
- data/lib/view_component/form/engine.rb +0 -2
- data/lib/view_component/form/version.rb +1 -1
- data/lib/view_component/form.rb +14 -2
- metadata +24 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5a9199cdd2815e1711c5799426cf9e8ba25ecd6352c34616c973a269894f4a71
|
4
|
+
data.tar.gz: 21f504ee76b9eaf898ac3a125bab5234fef641df64e04554a3bdf5d748185858
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 486ba4c74fd76b83e58c355234d0d65a2a06568634561d2a887b8623e115500c911ca810040f3fa5b41c66c37e3e80d58052065550281300bf7e355e635c3a76
|
7
|
+
data.tar.gz: 56d58c623a852b428d5b6440aeeb6a4f3c18c93ffd748a759588c0a5a4726b5e089517d48bdfebc2431447753c23a43b4612d639679f429d01f7208bcde9a87b
|
data/CHANGELOG.md
CHANGED
@@ -7,6 +7,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
7
7
|
## [Unreleased]
|
8
8
|
Nothing yet
|
9
9
|
|
10
|
+
## [0.2.1] - 2022-03-17
|
11
|
+
### Added
|
12
|
+
- Conditional ActionText in CI (#117)
|
13
|
+
|
14
|
+
### Fixed
|
15
|
+
- Fix broken gem initialization due to missing ViewComponent::Form constant (#114)
|
16
|
+
- Initialize empty WeekdaySelectComponent if Rails < 7 to fix Zeitwerk error (#115)
|
17
|
+
|
18
|
+
## [0.2.0] - 2022-03-02
|
19
|
+
### Added
|
20
|
+
- Test BuilderGenerator with generator\_spec (#64)
|
21
|
+
- Add HintComponent and ErrorMessageComponent (#98)
|
22
|
+
- Add `validation_context` option to `Form::Builder` (#101)
|
23
|
+
- Add `validators` helper to `FieldComponent` (#101)
|
24
|
+
- Add `optional?` and `required?` helpers to `FieldComponent` (#101)
|
25
|
+
- Add `label_text` helper (#103)
|
26
|
+
- Add `field_id` helper, backported from Rails 7.0 (#104)
|
27
|
+
- Add `weekday_select` helper (#105)
|
28
|
+
- Add README section about supported helpers (#106)
|
29
|
+
- Setup zeitwerk (#107)
|
30
|
+
- Add documentation for tests (#108)
|
31
|
+
|
32
|
+
## [0.1.3] - 2022-01-11
|
33
|
+
### Fixed
|
34
|
+
- Update dependencies for Rails 7.0.0 (#96)
|
35
|
+
- Bump to Rails 7 in Gemfile.lock (#99)
|
36
|
+
|
10
37
|
## [0.1.2] - 2021-12-07
|
11
38
|
### Added
|
12
39
|
- Add missing component specs (#75)
|
@@ -46,7 +73,10 @@ Nothing yet
|
|
46
73
|
- Add CHANGELOG (#50)
|
47
74
|
- Add CI (#2)
|
48
75
|
|
49
|
-
[Unreleased]: https://github.com/pantographe/view_component-form/compare/v0.1
|
76
|
+
[Unreleased]: https://github.com/pantographe/view_component-form/compare/v0.2.1...HEAD
|
77
|
+
[0.2.1]: https://github.com/pantographe/view_component-form/compare/v0.2.0...v0.2.1
|
78
|
+
[0.2.0]: https://github.com/pantographe/view_component-form/compare/v0.1.3...v0.2.0
|
79
|
+
[0.1.3]: https://github.com/pantographe/view_component-form/compare/v0.1.2...v0.1.3
|
50
80
|
[0.1.2]: https://github.com/pantographe/view_component-form/compare/v0.1.1...v0.1.2
|
51
81
|
[0.1.1]: https://github.com/pantographe/view_component-form/compare/v0.1.0...v0.1.1
|
52
82
|
[0.1.0]: https://github.com/pantographe/view_component-form/releases/tag/v0.1.0
|
data/README.md
CHANGED
@@ -48,7 +48,13 @@ Then call your helpers as usual:
|
|
48
48
|
<%= f.email_field :email %> <%# renders a ViewComponent::Form::EmailFieldComponent %>
|
49
49
|
|
50
50
|
<%= f.label :password %> <%# renders a ViewComponent::Form::LabelComponent %>
|
51
|
-
<%= f.password_field :password
|
51
|
+
<%= f.password_field :password, aria: { describedby: f.field_id(:password, :description) } %>
|
52
|
+
<%# renders a ViewComponent::Form::PasswordFieldComponent %>
|
53
|
+
<div id="<%= f.field_id(:title, :description) %>">
|
54
|
+
<%= f.hint :password, 'The password should be at least 8 characters long' %>
|
55
|
+
<%# renders a ViewComponent::Form::HintComponent %>
|
56
|
+
<%= f.error_message :password %> <%# renders a ViewComponent::Form::ErrorMessageComponent %>
|
57
|
+
</div>
|
52
58
|
<% end %>
|
53
59
|
```
|
54
60
|
|
@@ -69,7 +75,10 @@ It should work out of the box, but does nothing particularly interesting for now
|
|
69
75
|
<input type="email" value="john.doe@example.com" name="user[email]" id="user_email" />
|
70
76
|
|
71
77
|
<label for="user_password">Password</label>
|
72
|
-
<input type="password" name="user[password]" id="user_password" />
|
78
|
+
<input type="password" name="user[password]" id="user_password" aria-describedby="user_password_description" />
|
79
|
+
<div id="user_password_description">
|
80
|
+
<div>The password should be at least 8 characters long</div>
|
81
|
+
</div>
|
73
82
|
</form>
|
74
83
|
```
|
75
84
|
|
@@ -156,6 +165,151 @@ You can use the same approach to inject options, wrap the input in a `<div>`, et
|
|
156
165
|
|
157
166
|
We'll add more use cases to the documentation soon.
|
158
167
|
|
168
|
+
### Building your own components
|
169
|
+
|
170
|
+
When building your own ViewComponents for using in forms, it's recommended to inherit from `ViewComponent::Form::FieldComponent`, so you get access to the following helpers:
|
171
|
+
|
172
|
+
#### `#label_text`
|
173
|
+
|
174
|
+
Returns the translated text for the label of the field (looking up for `helpers.label.OBJECT.METHOD_NAME`), or humanized version of the method name if not available.
|
175
|
+
|
176
|
+
```rb
|
177
|
+
# app/components/custom/form/group_component.rb
|
178
|
+
class Custom::Form::GroupComponent < ViewComponent::Form::FieldComponent
|
179
|
+
end
|
180
|
+
```
|
181
|
+
|
182
|
+
```erb
|
183
|
+
<%# app/components/custom/form/group_component.html.erb %>
|
184
|
+
<div class="custom-form-group">
|
185
|
+
<label>
|
186
|
+
<%= label_text %><br />
|
187
|
+
<%= content %>
|
188
|
+
</label>
|
189
|
+
</div>
|
190
|
+
```
|
191
|
+
|
192
|
+
```erb
|
193
|
+
<%# app/views/users/_form.html.erb %>
|
194
|
+
<%= form_for @user do |f| %>
|
195
|
+
<%= f.group :first_name do %>
|
196
|
+
<%= f.text_field :first_name %>
|
197
|
+
<% end %>
|
198
|
+
<% end %>
|
199
|
+
```
|
200
|
+
|
201
|
+
```yml
|
202
|
+
# config/locales/en.yml
|
203
|
+
en:
|
204
|
+
helpers:
|
205
|
+
label:
|
206
|
+
user:
|
207
|
+
first_name: Your first name
|
208
|
+
```
|
209
|
+
|
210
|
+
Renders:
|
211
|
+
|
212
|
+
```html
|
213
|
+
<form class="edit_user" id="edit_user_1" action="/users/1" accept-charset="UTF-8" method="post">
|
214
|
+
<!-- ... -->
|
215
|
+
<label>
|
216
|
+
Your first name<br />
|
217
|
+
<input type="text" value="John" name="user[first_name]" id="user_first_name" />
|
218
|
+
</label>
|
219
|
+
</form>
|
220
|
+
```
|
221
|
+
|
222
|
+
#### Validations
|
223
|
+
|
224
|
+
Let's consider the following model for the examples below.
|
225
|
+
|
226
|
+
```rb
|
227
|
+
# app/models/user.rb
|
228
|
+
class User < ActiveRecord::Base
|
229
|
+
validates :first_name, presence: true, length: { minimum: 2, maximum: 255 }
|
230
|
+
end
|
231
|
+
```
|
232
|
+
|
233
|
+
##### Accessing validations with `#validators`
|
234
|
+
|
235
|
+
Returns all validators for the method name.
|
236
|
+
|
237
|
+
```rb
|
238
|
+
# app/components/custom/form/group_component.rb
|
239
|
+
class Custom::Form::GroupComponent < ViewComponent::Form::FieldComponent
|
240
|
+
private
|
241
|
+
|
242
|
+
def validation_hint
|
243
|
+
if length_validator
|
244
|
+
"between #{length_validator.options[:minimum]} and #{length_validator.options[:maximum]} chars"
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def length_validator
|
249
|
+
validators.find { |v| v.is_a?(ActiveModel::Validations::LengthValidator) }
|
250
|
+
end
|
251
|
+
end
|
252
|
+
```
|
253
|
+
|
254
|
+
```erb
|
255
|
+
<%# app/components/custom/form/group_component.html.erb %>
|
256
|
+
<div class="custom-form-group">
|
257
|
+
<label>
|
258
|
+
<%= label_text %> (<%= validation_hint %>)<br />
|
259
|
+
<%= content %>
|
260
|
+
</label>
|
261
|
+
</div>
|
262
|
+
```
|
263
|
+
|
264
|
+
##### Using `#required?` and `#optional?`
|
265
|
+
|
266
|
+
```erb
|
267
|
+
<%# app/components/custom/form/group_component.html.erb %>
|
268
|
+
<div class="custom-form-group">
|
269
|
+
<label>
|
270
|
+
<%= label_text %><%= " (required)" if required? %><br />
|
271
|
+
<%= content %>
|
272
|
+
</label>
|
273
|
+
</div>
|
274
|
+
```
|
275
|
+
|
276
|
+
##### Validation contexts
|
277
|
+
|
278
|
+
When using [validation contexts](https://guides.rubyonrails.org/active_record_validations.html#on), you can specify a context to the helpers above.
|
279
|
+
|
280
|
+
```rb
|
281
|
+
# app/models/user.rb
|
282
|
+
class User < ActiveRecord::Base
|
283
|
+
validates :first_name, presence: true, length: { minimum: 2, maximum: 255 }
|
284
|
+
validates :email, presence: true, on: :registration
|
285
|
+
end
|
286
|
+
```
|
287
|
+
|
288
|
+
```erb
|
289
|
+
<%# app/views/users/_form_.html.erb %>
|
290
|
+
<%= form_with model: @user,
|
291
|
+
builder: ViewComponent::Form::Builder,
|
292
|
+
validation_context: :registration do |f| %>
|
293
|
+
<%= f.group :email do %>
|
294
|
+
<%= f.email_field :email %>
|
295
|
+
<% end %>
|
296
|
+
<% end %>
|
297
|
+
```
|
298
|
+
|
299
|
+
In this case, `ViewComponent::Form::Builder` accepts a `validation_context` option and passes it as a default value to the `#validators`, `#required?` and `#optional?` helpers.
|
300
|
+
|
301
|
+
Alternatively, you can pass the context to the helpers:
|
302
|
+
|
303
|
+
```erb
|
304
|
+
<%= "(required)" if required?(context: :registration) %>
|
305
|
+
```
|
306
|
+
|
307
|
+
```rb
|
308
|
+
def length_validator
|
309
|
+
validators(context: :registration).find { |v| v.is_a?(ActiveModel::Validations::LengthValidator) }
|
310
|
+
end
|
311
|
+
```
|
312
|
+
|
159
313
|
### Using your form components without a backing model
|
160
314
|
|
161
315
|
If you want to ensure that your fields display consistently across your app, you'll need to lean on Rails' own helpers. You may be used to using form tag helpers such as `text_field_tag` to generate tags, or even writing out plain HTML tags. These can't be integrated with a form builder, so they won't offer you the benefits of this gem.
|
@@ -167,6 +321,65 @@ You'll most likely want to use either:
|
|
167
321
|
|
168
322
|
[`fields_for`](https://api.rubyonrails.org/v6.1.4/classes/ActionView/Helpers/FormHelper.html#method-i-fields_for) may also be of interest. To make consistent use of `view_component-form`, you'll want to be using these three helpers to build your forms wherever possible.
|
169
323
|
|
324
|
+
## Supported helpers
|
325
|
+
|
326
|
+
The following helpers are currently supported by `ViewComponent::Form`.
|
327
|
+
|
328
|
+
### `ActionView::Helpers::FormBuilder`
|
329
|
+
|
330
|
+
**Supported:** `button` `check_box` `collection_check_boxes` `collection_radio_buttons` `collection_select` `color_field` `date_field` `date_select` `datetime_field` `datetime_local_field` `datetime_select` `email_field` `fields` `fields_for` `file_field` `field_id` `grouped_collection_select` `hidden_field` `month_field` `number_field` `password_field` `phone_field` `radio_button` `range_field` `search_field` `select` `submit` `telephone_field` `text_area` `text_field` `time_field` `time_select` `time_zone_select` `to_model` `to_partial_path` `url_field` `week_field` `weekday_select`
|
331
|
+
|
332
|
+
**Partially supported:** `label` (blocks not supported) `rich_text_area` (untested)
|
333
|
+
|
334
|
+
**Unsupported for now:** `field_name`
|
335
|
+
|
336
|
+
### Specific to `ViewComponent::Form`
|
337
|
+
|
338
|
+
**Supported:** `error_message` `hint`
|
339
|
+
|
340
|
+
## Testing your components
|
341
|
+
|
342
|
+
### RSpec
|
343
|
+
|
344
|
+
#### Configuration
|
345
|
+
|
346
|
+
This assumes your already have read and configured [tests for `view_component`](https://viewcomponent.org/guide/testing.html#rspec-configuration).
|
347
|
+
|
348
|
+
```rb
|
349
|
+
# spec/rails_helper.rb
|
350
|
+
require "view_component/test_helpers"
|
351
|
+
require "view_component/form/test_helpers"
|
352
|
+
require "capybara/rspec"
|
353
|
+
|
354
|
+
RSpec.configure do |config|
|
355
|
+
config.include ViewComponent::TestHelpers, type: :component
|
356
|
+
config.include ViewComponent::Form::TestHelpers, type: :component
|
357
|
+
config.include Capybara::RSpecMatchers, type: :component
|
358
|
+
end
|
359
|
+
```
|
360
|
+
|
361
|
+
#### Example
|
362
|
+
|
363
|
+
```rb
|
364
|
+
# spec/components/form/text_field_component_spec.rb
|
365
|
+
RSpec.describe Form::TextFieldComponent, type: :component do
|
366
|
+
let(:object) { User.new } # replace with a model of your choice
|
367
|
+
let(:form) { form_with(object) }
|
368
|
+
let(:options) { {} }
|
369
|
+
|
370
|
+
let(:component) { render_inline(described_class.new(form, object_name, :first_name, options)) }
|
371
|
+
|
372
|
+
context "with simple args" do
|
373
|
+
it do
|
374
|
+
expect(component.to_html)
|
375
|
+
.to have_tag("input", with: { name: "user[first_name]", id: "user_first_name", type: "text" })
|
376
|
+
end
|
377
|
+
end
|
378
|
+
end
|
379
|
+
```
|
380
|
+
|
381
|
+
For more complex components, we recommend the [`rspec-html-matchers` gem](https://github.com/kucaahbe/rspec-html-matchers).
|
382
|
+
|
170
383
|
## Development
|
171
384
|
|
172
385
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -7,10 +7,7 @@ module ViewComponent
|
|
7
7
|
attr_accessor :default_options
|
8
8
|
end
|
9
9
|
|
10
|
-
if Gem::Version.new(Rails::VERSION::STRING) < Gem::Version.new("6.1")
|
11
|
-
require "view_component/form/class_names_helper"
|
12
|
-
include ClassNamesHelper
|
13
|
-
end
|
10
|
+
include ClassNamesHelper if Gem::Version.new(Rails::VERSION::STRING) < Gem::Version.new("6.1")
|
14
11
|
|
15
12
|
attr_reader :form, :object_name, :options
|
16
13
|
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ViewComponent
|
4
|
+
module Form
|
5
|
+
class ErrorMessageComponent < FieldComponent
|
6
|
+
class_attribute :tag, instance_reader: false, instance_writer: false, instance_accessor: false,
|
7
|
+
instance_predicate: false
|
8
|
+
|
9
|
+
self.tag = :div
|
10
|
+
|
11
|
+
def call
|
12
|
+
tag.public_send(self.class.tag, messages, **options)
|
13
|
+
end
|
14
|
+
|
15
|
+
def render?
|
16
|
+
method_errors?
|
17
|
+
end
|
18
|
+
|
19
|
+
def messages
|
20
|
+
safe_join(method_errors, tag.br)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -8,6 +8,8 @@ module ViewComponent
|
|
8
8
|
|
9
9
|
attr_reader :method_name
|
10
10
|
|
11
|
+
delegate :validation_context, to: :form
|
12
|
+
|
11
13
|
def initialize(form, object_name, method_name, options = {})
|
12
14
|
# See: https://github.com/rails/rails/blob/83217025a171593547d1268651b446d3533e2019/actionview/lib/action_view/helpers/tags/base.rb#L13
|
13
15
|
@method_name = method_name.to_s.dup
|
@@ -60,6 +62,36 @@ module ViewComponent
|
|
60
62
|
end
|
61
63
|
end
|
62
64
|
|
65
|
+
# From https://github.com/rails/rails/blob/497ab719d04a2d505f4d6a76c9d359b3d7f8e502/actionview/lib/action_view/helpers/tags/label.rb#L18-L27
|
66
|
+
def label_text
|
67
|
+
content ||= ActionView::Helpers::Tags::Translator.new(object, object_name, method_name,
|
68
|
+
scope: "helpers.label").translate
|
69
|
+
content ||= method_name.humanize
|
70
|
+
content
|
71
|
+
end
|
72
|
+
|
73
|
+
def optional?(context: validation_context)
|
74
|
+
return nil if object.nil?
|
75
|
+
|
76
|
+
!required?(context: context)
|
77
|
+
end
|
78
|
+
|
79
|
+
def required?(context: validation_context)
|
80
|
+
return nil if object.nil?
|
81
|
+
|
82
|
+
validators(context: context).any?(ActiveModel::Validations::PresenceValidator)
|
83
|
+
end
|
84
|
+
|
85
|
+
def validators(context: validation_context)
|
86
|
+
method_validators.select do |validator|
|
87
|
+
if context.nil?
|
88
|
+
validator.options[:on].blank?
|
89
|
+
else
|
90
|
+
Array(validator.options[:on]).include?(context&.to_sym)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
63
95
|
private
|
64
96
|
|
65
97
|
def singular_association_method_name
|
@@ -69,6 +101,14 @@ module ViewComponent
|
|
69
101
|
def collection_association_method_name
|
70
102
|
@collection_association_method_name ||= method_name.to_s.sub(/_ids$/, "").pluralize.to_sym
|
71
103
|
end
|
104
|
+
|
105
|
+
def method_validators
|
106
|
+
@method_validators ||= if object.nil?
|
107
|
+
[]
|
108
|
+
else
|
109
|
+
object.class.validators_on(method_name)
|
110
|
+
end
|
111
|
+
end
|
72
112
|
end
|
73
113
|
end
|
74
114
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ViewComponent
|
4
|
+
module Form
|
5
|
+
class HintComponent < FieldComponent
|
6
|
+
class_attribute :tag, instance_reader: false, instance_writer: false, instance_accessor: false,
|
7
|
+
instance_predicate: false
|
8
|
+
attr_reader :attribute_content
|
9
|
+
|
10
|
+
self.tag = :div
|
11
|
+
|
12
|
+
def initialize(form, object_name, method_name, content_or_options = nil, options = nil)
|
13
|
+
options ||= {}
|
14
|
+
|
15
|
+
content_is_options = content_or_options.is_a?(Hash)
|
16
|
+
if content_is_options
|
17
|
+
options.merge! content_or_options
|
18
|
+
@attribute_content = nil
|
19
|
+
else
|
20
|
+
@attribute_content = content_or_options
|
21
|
+
end
|
22
|
+
|
23
|
+
super(form, object_name, method_name, options)
|
24
|
+
end
|
25
|
+
|
26
|
+
def call
|
27
|
+
content_or_options = content.presence || attribute_content.presence
|
28
|
+
|
29
|
+
tag.public_send(self.class.tag, content_or_options, **options)
|
30
|
+
end
|
31
|
+
|
32
|
+
def render?
|
33
|
+
content.present? || attribute_content.present?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ViewComponent
|
4
|
+
module Form
|
5
|
+
class WeekdaySelectComponent < FieldComponent
|
6
|
+
attr_reader :html_options
|
7
|
+
|
8
|
+
def initialize(form, object_name, method_name, options = {}, html_options = {})
|
9
|
+
@html_options = html_options
|
10
|
+
|
11
|
+
super(form, object_name, method_name, options)
|
12
|
+
|
13
|
+
set_html_options!
|
14
|
+
end
|
15
|
+
|
16
|
+
def call # rubocop:disable Metrics/MethodLength
|
17
|
+
if Rails::VERSION::MAJOR >= 7 # rubocop:disable Style/GuardClause
|
18
|
+
ActionView::Helpers::Tags::WeekdaySelect.new(
|
19
|
+
object_name,
|
20
|
+
method_name,
|
21
|
+
@view_context,
|
22
|
+
options,
|
23
|
+
html_options
|
24
|
+
).render
|
25
|
+
else
|
26
|
+
raise NotImplementedError, "#{self.class} is only available in Rails >= 7"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
protected
|
31
|
+
|
32
|
+
def set_html_options!
|
33
|
+
@html_options[:class] = class_names(html_options[:class], html_class)
|
34
|
+
@html_options.delete(:class) if @html_options[:class].blank?
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "action_view"
|
4
|
-
|
5
3
|
module ViewComponent
|
6
4
|
module Form
|
7
5
|
class Builder < ActionView::Helpers::FormBuilder
|
@@ -29,10 +27,14 @@ module ViewComponent
|
|
29
27
|
end
|
30
28
|
end
|
31
29
|
|
30
|
+
attr_reader :validation_context
|
31
|
+
|
32
32
|
def initialize(*)
|
33
33
|
@__component_klass_cache = {}
|
34
34
|
|
35
35
|
super
|
36
|
+
|
37
|
+
@validation_context = options[:validation_context]
|
36
38
|
end
|
37
39
|
|
38
40
|
(field_helpers - %i[
|
@@ -189,6 +191,33 @@ module ViewComponent
|
|
189
191
|
end
|
190
192
|
end
|
191
193
|
|
194
|
+
def error_message(method, options = {})
|
195
|
+
render_component(:error_message, @object_name, method, objectify_options(options))
|
196
|
+
end
|
197
|
+
|
198
|
+
def hint(method, text = nil, options = {}, &block)
|
199
|
+
render_component(:hint, @object_name, method, text, objectify_options(options), &block)
|
200
|
+
end
|
201
|
+
|
202
|
+
# Backport field_id from Rails 7.0
|
203
|
+
if Rails::VERSION::MAJOR < 7
|
204
|
+
def field_id(method_name, *suffixes, namespace: @options[:namespace], index: @index)
|
205
|
+
object_name = object_name.model_name.singular if object_name.respond_to?(:model_name)
|
206
|
+
|
207
|
+
sanitized_object_name = object_name.to_s.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").delete_suffix("_")
|
208
|
+
|
209
|
+
sanitized_method_name = method_name.to_s.delete_suffix("?")
|
210
|
+
|
211
|
+
[
|
212
|
+
namespace,
|
213
|
+
sanitized_object_name.presence,
|
214
|
+
(index unless sanitized_object_name.empty?),
|
215
|
+
sanitized_method_name,
|
216
|
+
*suffixes
|
217
|
+
].tap(&:compact!).join("_")
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
192
221
|
private
|
193
222
|
|
194
223
|
def render_component(component_name, *args, &block)
|
data/lib/view_component/form.rb
CHANGED
@@ -1,5 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require "view_component"
|
4
|
+
require "zeitwerk"
|
5
|
+
|
6
|
+
module ViewComponent
|
7
|
+
module Form
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
loader = Zeitwerk::Loader.for_gem
|
12
|
+
form = "#{__dir__}/form.rb"
|
13
|
+
loader.ignore(form)
|
14
|
+
loader.push_dir("#{__dir__}/form", namespace: ViewComponent::Form)
|
15
|
+
loader.setup
|
16
|
+
|
4
17
|
require_relative "form/engine"
|
5
|
-
require_relative "form/builder"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: view_component-form
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pantographe
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-03-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actionview
|
@@ -19,7 +19,7 @@ dependencies:
|
|
19
19
|
version: 6.0.0
|
20
20
|
- - "<"
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: '7.
|
22
|
+
version: '7.1'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -29,7 +29,7 @@ dependencies:
|
|
29
29
|
version: 6.0.0
|
30
30
|
- - "<"
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: '7.
|
32
|
+
version: '7.1'
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
34
|
name: activesupport
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -39,7 +39,7 @@ dependencies:
|
|
39
39
|
version: 6.0.0
|
40
40
|
- - "<"
|
41
41
|
- !ruby/object:Gem::Version
|
42
|
-
version: '7.
|
42
|
+
version: '7.1'
|
43
43
|
type: :runtime
|
44
44
|
prerelease: false
|
45
45
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -49,7 +49,7 @@ dependencies:
|
|
49
49
|
version: 6.0.0
|
50
50
|
- - "<"
|
51
51
|
- !ruby/object:Gem::Version
|
52
|
-
version: '7.
|
52
|
+
version: '7.1'
|
53
53
|
- !ruby/object:Gem::Dependency
|
54
54
|
name: view_component
|
55
55
|
requirement: !ruby/object:Gem::Requirement
|
@@ -70,6 +70,20 @@ dependencies:
|
|
70
70
|
- - "<"
|
71
71
|
- !ruby/object:Gem::Version
|
72
72
|
version: '3.0'
|
73
|
+
- !ruby/object:Gem::Dependency
|
74
|
+
name: zeitwerk
|
75
|
+
requirement: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - "~>"
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '2.5'
|
80
|
+
type: :runtime
|
81
|
+
prerelease: false
|
82
|
+
version_requirements: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - "~>"
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '2.5'
|
73
87
|
description: Rails FormBuilder for ViewComponent
|
74
88
|
email:
|
75
89
|
- oss@pantographe.studio
|
@@ -92,9 +106,11 @@ files:
|
|
92
106
|
- app/components/view_component/form/datetime_local_field_component.rb
|
93
107
|
- app/components/view_component/form/datetime_select_component.rb
|
94
108
|
- app/components/view_component/form/email_field_component.rb
|
109
|
+
- app/components/view_component/form/error_message_component.rb
|
95
110
|
- app/components/view_component/form/field_component.rb
|
96
111
|
- app/components/view_component/form/file_field_component.rb
|
97
112
|
- app/components/view_component/form/grouped_collection_select_component.rb
|
113
|
+
- app/components/view_component/form/hint_component.rb
|
98
114
|
- app/components/view_component/form/label_component.rb
|
99
115
|
- app/components/view_component/form/month_field_component.rb
|
100
116
|
- app/components/view_component/form/number_field_component.rb
|
@@ -113,6 +129,7 @@ files:
|
|
113
129
|
- app/components/view_component/form/time_zone_select_component.rb
|
114
130
|
- app/components/view_component/form/url_field_component.rb
|
115
131
|
- app/components/view_component/form/week_field_component.rb
|
132
|
+
- app/components/view_component/form/weekday_select_component.rb
|
116
133
|
- lib/generators/vcf/builder/builder_generator.rb
|
117
134
|
- lib/generators/vcf/builder/templates/builder.rb.erb
|
118
135
|
- lib/view_component/form.rb
|
@@ -145,7 +162,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
145
162
|
- !ruby/object:Gem::Version
|
146
163
|
version: '0'
|
147
164
|
requirements: []
|
148
|
-
rubygems_version: 3.2.
|
165
|
+
rubygems_version: 3.2.32
|
149
166
|
signing_key:
|
150
167
|
specification_version: 4
|
151
168
|
summary: Rails FormBuilder for ViewComponent
|