Skip to content

Commit 082cbf2

Browse files
committed
Accept HTML attributes as kwargs on components
Components now accept HTML attributes directly as keyword arguments instead of requiring the `attributes:` wrapper. The old `attributes:` style still works but emits a deprecation warning. Before: MyInput.new(field, attributes: { class: "foo" }) After: MyInput.new(field, class: "foo")
1 parent 91d68f2 commit 082cbf2

File tree

12 files changed

+108
-32
lines changed

12 files changed

+108
-32
lines changed

CHANGELOG.md

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,37 @@
11
## [Unreleased]
22

3+
### Changed
4+
5+
- **Deprecation**: Components now accept HTML attributes as keyword arguments directly instead of wrapping them in `attributes:`. The old `attributes:` keyword still works but emits a deprecation warning and will be removed in a future version.
6+
7+
```ruby
8+
# Before
9+
MyInput.new(field, attributes: { class: "form-input" })
10+
11+
# After
12+
MyInput.new(field, class: "form-input")
13+
```
14+
15+
If you have custom components that override `initialize`, update them to use `**attributes`:
16+
17+
```ruby
18+
# Before
19+
class MyRadio < Superform::Rails::Components::Field
20+
def initialize(field, value:, attributes: {})
21+
super(field, attributes: attributes)
22+
@value = value
23+
end
24+
end
25+
26+
# After
27+
class MyRadio < Superform::Rails::Components::Field
28+
def initialize(field, value:, **attributes)
29+
super(field, **attributes)
30+
@value = value
31+
end
32+
end
33+
```
34+
335
## [0.6.1] - 2025-08-28
436

537
### Breaking Changes
@@ -106,7 +138,7 @@ Update component class names in your custom form classes:
106138

107139
class Field < Superform::Rails::Form::Field
108140
def input(**attributes)
109-
MyInput.new(self, attributes: attributes)
141+
MyInput.new(self, **attributes)
110142
end
111143
end
112144
```

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ class Components::Form < Superform::Rails::Form
179179
# Redefining the base Field class lets us override every field component.
180180
class Field < Superform::Rails::Form::Field
181181
def input(**attributes)
182-
MyInput.new(self, attributes: attributes)
182+
MyInput.new(self, **attributes)
183183
end
184184
end
185185

@@ -469,7 +469,7 @@ class AdminForm < Components::Form
469469

470470
class Field < Field
471471
def tooltip_input(**attributes)
472-
AdminInput.new(self, attributes: attributes)
472+
AdminInput.new(self, **attributes)
473473
end
474474
end
475475
end

lib/generators/superform/install/templates/base.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class Field < self::Field
1515
#
1616
# # Return your own component if you're doing more complicated things.
1717
# def autocomplete(**attributes)
18-
# Components::Autocomplete.new(field, attributes:)
18+
# Components::Autocomplete.new(field, **attributes)
1919
# end
2020
end
2121

lib/superform/rails/components/base.rb

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,16 @@ class Base < Component
66

77
delegate :dom, to: :field
88

9-
def initialize(field, attributes: {})
9+
def initialize(field, attributes: nil, **attributes_kwargs)
1010
@field = field
11-
@attributes = attributes
11+
if attributes
12+
warn "[DEPRECATION] Passing `attributes:` keyword to #{self.class.name} is deprecated. " \
13+
"Pass HTML attributes as keyword arguments directly instead: " \
14+
"#{self.class.name}.new(field, **attributes)"
15+
@attributes = attributes.merge(attributes_kwargs)
16+
else
17+
@attributes = attributes_kwargs
18+
end
1219
end
1320

1421
def field_attributes

lib/superform/rails/components/radio.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ module Superform
22
module Rails
33
module Components
44
class Radio < Field
5-
def initialize(field, value:, attributes: {})
6-
super(field, attributes: attributes)
5+
def initialize(field, value:, **attributes)
6+
super(field, **attributes)
77
@value = value
88
end
99

lib/superform/rails/components/select.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ module Rails
33
module Components
44
class Select < Field
55
def initialize(
6-
*,
6+
field,
77
options: [],
88
collection: nil,
99
multiple: false,
10-
**,
10+
**attributes,
1111
&
1212
)
13-
super(*, **, &)
13+
super(field, **attributes, &)
1414

1515
# Handle deprecated collection parameter
1616
if collection && options.empty?

lib/superform/rails/field.rb

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ module Rails
1212
# end
1313
#
1414
# class Field < Field
15-
# def label(**, &)
16-
# MyLabel.new(self, **, &)
15+
# def label(**attributes, &)
16+
# MyLabel.new(self, **attributes, &)
1717
# end
1818
#
1919
# def input(class: nil, **)
@@ -26,31 +26,31 @@ module Rails
2626
# Now all calls to `label` will have the `text-bold` class applied to it.
2727
class Field < Superform::Field
2828
def button(**attributes)
29-
Components::Button.new(field, attributes:)
29+
Components::Button.new(field, **attributes)
3030
end
3131

3232
def input(**attributes)
33-
Components::Input.new(field, attributes:)
33+
Components::Input.new(field, **attributes)
3434
end
3535

3636
def checkbox(**attributes)
37-
Components::Checkbox.new(field, attributes:)
37+
Components::Checkbox.new(field, **attributes)
3838
end
3939

4040
def label(**attributes, &)
41-
Components::Label.new(field, attributes:, &)
41+
Components::Label.new(field, **attributes, &)
4242
end
4343

4444
def textarea(**attributes)
45-
Components::Textarea.new(field, attributes:)
45+
Components::Textarea.new(field, **attributes)
4646
end
4747

4848
def select(*options, multiple: false, **attributes, &)
4949
Components::Select.new(
5050
field,
51-
attributes:,
5251
options:,
5352
multiple:,
53+
**attributes,
5454
&
5555
)
5656
end
@@ -144,7 +144,7 @@ def file(*, **)
144144
end
145145

146146
def radio(value, **attributes)
147-
Components::Radio.new(field, value: value, attributes: attributes)
147+
Components::Radio.new(field, value:, **attributes)
148148
end
149149

150150
# Rails compatibility aliases
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
RSpec.describe Superform::Rails::Components::Base do
2+
let(:object) { double('object', name: "Test") }
3+
let(:field) { Superform::Rails::Field.new(:name, parent: nil, object: object) }
4+
5+
describe 'new kwargs style' do
6+
it 'accepts HTML attributes as kwargs' do
7+
component = described_class.new(field, class: "form-input", id: "custom")
8+
attrs = component.send(:attributes)
9+
expect(attrs[:class]).to eq("form-input")
10+
expect(attrs[:id]).to eq("custom")
11+
end
12+
end
13+
14+
describe 'legacy attributes: style' do
15+
it 'accepts HTML attributes via attributes keyword with deprecation warning' do
16+
component = nil
17+
expect {
18+
component = described_class.new(field, attributes: { class: "form-input", id: "custom" })
19+
}.to output(/DEPRECATION/).to_stderr
20+
attrs = component.send(:attributes)
21+
expect(attrs[:class]).to eq("form-input")
22+
expect(attrs[:id]).to eq("custom")
23+
end
24+
end
25+
26+
describe 'mixed style' do
27+
it 'merges attributes: keyword with kwargs' do
28+
component = nil
29+
expect {
30+
component = described_class.new(field, attributes: { class: "form-input" }, id: "custom")
31+
}.to output(/DEPRECATION/).to_stderr
32+
attrs = component.send(:attributes)
33+
expect(attrs[:class]).to eq("form-input")
34+
expect(attrs[:id]).to eq("custom")
35+
end
36+
end
37+
end

spec/superform/rails/components/checkbox_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
let(:field) do
66
Superform::Rails::Field.new(:featured, parent: nil, object: object)
77
end
8-
let(:component) { described_class.new(field, attributes: {}) }
8+
let(:component) { described_class.new(field) }
99

1010
subject { render(component) }
1111

@@ -63,7 +63,7 @@
6363
all_roles = [[1, "Admin"], [2, "Editor"], [3, "Viewer"]]
6464
html = ""
6565
all_roles.each do |id, _name|
66-
html += render(described_class.new(field, attributes: { value: id }))
66+
html += render(described_class.new(field, value: id))
6767
end
6868

6969
expect(html).to include('name="role_ids[]"')

spec/superform/rails/components/input_component_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
{}
1212
end
1313
let(:component) do
14-
described_class.new(field, attributes: attributes)
14+
described_class.new(field, **attributes)
1515
end
1616
subject { component }
1717

0 commit comments

Comments
 (0)