Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ nav_order: 6

## main

* Fix stale content cache when slots are accessed before `render_in`.

*Jared Armstrong*

* Add rubocop-view_component to resources.

*Andy Waite*
Expand Down
1 change: 1 addition & 0 deletions lib/view_component/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ def render_in(view_context, &block)
end

@__vc_content_evaluated = false
remove_instance_variable(:@__vc_content) if defined?(@__vc_content)
@__vc_render_in_block = block
@view_context.instance_variable_set(:@virtual_path, virtual_path)

Expand Down
12 changes: 12 additions & 0 deletions test/sandbox/app/components/slot_with_content_block_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# frozen_string_literal: true

class SlotWithContentBlockComponent < ViewComponent::Base
renders_one :header

def call
out = +""
out << content_tag(:div, header, class: "header") if header?
out << content_tag(:div, content, class: "body") if content?
out.html_safe
end
end
17 changes: 17 additions & 0 deletions test/sandbox/test/slotable_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,23 @@ def test_slot_name_methods_are_not_shared_accross_components
assert_not_equal SlotsComponent.instance_method(:title).owner, SlotNameOverrideComponent::OtherComponent.instance_method(:title).owner
end

def test_slot_predicate_before_render_does_not_poison_content_cache
component = SlotWithContentBlockComponent.new
component.with_header { "My Header" }

# Accessing a slot predicate before render_in triggers content evaluation
# via __vc_get_slot. Without the fix, this caches a nil @__vc_content that
# persists through render_in, causing the content block to be silently ignored.
assert component.header?

render_inline(component) do
"Body content from block"
end

assert_selector(".header", text: "My Header")
assert_selector(".body", text: "Body content from block")
end

def test_allows_marking_slot_as_last
render_inline(LastItemComponent.new) do |component|
component.with_item("animal")
Expand Down
Loading