Skip to content

Commit d514f5d

Browse files
Add slot methods to a module instead of the component class itself (#2040)
* Add distinct GeneratedSlotMethods module to ViewComponents and define `#{slot}` and `#{slot?}` methods on this module, allowing these methods to be overridden with access to `super` implementation. * Update docs/CONTRIBUTING.md * Update docs/CHANGELOG.md --------- Co-authored-by: Joel Hawksley <[email protected]> Co-authored-by: Joel Hawksley <[email protected]>
1 parent 80c7c8e commit d514f5d

File tree

6 files changed

+90
-8
lines changed

6 files changed

+90
-8
lines changed

docs/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ nav_order: 5
1010

1111
## main
1212

13+
* Allow overridden slot methods to use `super`.
14+
15+
*Andrew Schwartz*
16+
1317
* Add Rails engine support to generators.
1418

1519
*Tomasz Kowalewski*

docs/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ ViewComponent is built by over a hundred members of the community, including:
182182
<img src="https://avatars.githubusercontent.com/nicolas-brousse?s=64" alt="nicolas-brousse" width="32" />
183183
<img src="https://avatars.githubusercontent.com/nielsslot?s=64" alt="nshki" width="32" />
184184
<img src="https://avatars.githubusercontent.com/nshki?s=64" alt="nshki" width="32" />
185+
<img src="https://avatars.githubusercontent.com/ozydingo?s=64" alt="ozydingo" width="32" />
185186
<img src="https://avatars.githubusercontent.com/patrickarnett?s=64" alt="patrickarnett" width="32" />
186187
<img src="https://avatars.githubusercontent.com/rainerborene?s=64" alt="rainerborene" width="32" />
187188
<img src="https://avatars.githubusercontent.com/rdavid1099?s=64" alt="rdavid1099" width="32" />

lib/view_component/slotable.rb

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,11 @@ def renders_one(slot_name, callable = nil)
8989
end
9090
ruby2_keywords(setter_method_name) if respond_to?(:ruby2_keywords, true)
9191

92-
define_method slot_name do
92+
self::GeneratedSlotMethods.define_method slot_name do
9393
get_slot(slot_name)
9494
end
9595

96-
define_method :"#{slot_name}?" do
96+
self::GeneratedSlotMethods.define_method :"#{slot_name}?" do
9797
get_slot(slot_name).present?
9898
end
9999

@@ -176,11 +176,11 @@ def renders_many(slot_name, callable = nil)
176176
end
177177
end
178178

179-
define_method slot_name do
179+
self::GeneratedSlotMethods.define_method slot_name do
180180
get_slot(slot_name)
181181
end
182182

183-
define_method :"#{slot_name}?" do
183+
self::GeneratedSlotMethods.define_method :"#{slot_name}?" do
184184
get_slot(slot_name).present?
185185
end
186186

@@ -199,19 +199,28 @@ def slot_type(slot_name)
199199
end
200200
end
201201

202-
# Clone slot configuration into child class
203-
# see #test_slots_pollution
204202
def inherited(child)
203+
# Clone slot configuration into child class
204+
# see #test_slots_pollution
205205
child.registered_slots = registered_slots.clone
206+
207+
# Add a module for slot methods, allowing them to be overriden by the component class
208+
# see #test_slot_name_can_be_overriden
209+
unless child.const_defined?(:GeneratedSlotMethods, false)
210+
generated_slot_methods = Module.new
211+
child.const_set(:GeneratedSlotMethods, generated_slot_methods)
212+
child.include generated_slot_methods
213+
end
214+
206215
super
207216
end
208217

209218
def register_polymorphic_slot(slot_name, types, collection:)
210-
define_method(slot_name) do
219+
self::GeneratedSlotMethods.define_method(slot_name) do
211220
get_slot(slot_name)
212221
end
213222

214-
define_method(:"#{slot_name}?") do
223+
self::GeneratedSlotMethods.define_method(:"#{slot_name}?") do
215224
get_slot(slot_name).present?
216225
end
217226

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<div class="card <%= @classes %>">
2+
<% if title? %>
3+
<div class="title">
4+
<%= title %>
5+
</div>
6+
<% end %>
7+
</div>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# frozen_string_literal: true
2+
3+
class SlotNameOverrideComponent < ViewComponent::Base
4+
renders_one :title
5+
6+
def initialize(title: nil)
7+
@title = title
8+
end
9+
10+
def title
11+
@title || super
12+
end
13+
14+
def title?
15+
@title.present? || super
16+
end
17+
end
18+
19+
class SlotNameOverrideComponent::OtherComponent < ViewComponent::Base
20+
renders_one :title
21+
end
22+
23+
class SlotNameOverrideComponent::SubComponent < SlotNameOverrideComponent
24+
def title
25+
super.upcase
26+
end
27+
end

test/sandbox/test/slotable_test.rb

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -785,4 +785,38 @@ def test_slotable_default_instance
785785

786786
assert_text "hello,world!", count: 1
787787
end
788+
789+
def test_slot_name_can_be_overriden
790+
# Uses overridden `title` slot method
791+
render_inline(SlotNameOverrideComponent.new(title: "Simple Title"))
792+
793+
assert_selector(".title", text: "Simple Title")
794+
end
795+
796+
def test_slot_name_override_can_use_super
797+
# Uses standard `title` slot method via `super`
798+
render_inline(SlotNameOverrideComponent.new) do |component|
799+
component.with_title do
800+
"Block Title with More Complexity"
801+
end
802+
end
803+
804+
assert_selector(".title", text: "Block Title with More Complexity")
805+
end
806+
807+
def overriden_slot_name_predicate_returns_false_when_not_set
808+
render_inline(SlotNameOverrideComponent.new)
809+
810+
refute_selector(".title")
811+
end
812+
813+
def test_overridden_slot_name_can_be_inherited
814+
render_inline(SlotNameOverrideComponent::SubComponent.new(title: "lowercase"))
815+
816+
assert_selector(".title", text: "LOWERCASE")
817+
end
818+
819+
def test_slot_name_methods_are_not_shared_accross_components
820+
assert_not_equal SlotsComponent.instance_method(:title).owner, SlotNameOverrideComponent::OtherComponent.instance_method(:title).owner
821+
end
788822
end

0 commit comments

Comments
 (0)