Skip to content

Commit

Permalink
Ensure content is rendered correctly for forwarded slots (#2016)
Browse files Browse the repository at this point in the history
* WIP

* Add benchmark

* Add benchmark

* Clean up benchmark; remove old slot content impl

* Fix bad merge

* Prevent warnings

* Add CHANGELOG entry

* Update docs/CHANGELOG.md

Co-authored-by: Hans Lemuet <Spone@users.noreply.github.com>

---------

Co-authored-by: Hans Lemuet <Spone@users.noreply.github.com>
  • Loading branch information
camertron and Spone authored Apr 17, 2024
1 parent 19df77c commit 719c28a
Show file tree
Hide file tree
Showing 9 changed files with 107 additions and 1 deletion.
4 changes: 4 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ task :translatable_benchmark do
ruby "./performance/translatable_benchmark.rb"
end

task :slots_benchmark do
ruby "./performance/slots_benchmark.rb"
end

namespace :coverage do
task :report do
require "simplecov"
Expand Down
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: 5

## main

* Ensure content is rendered correctly for forwarded slots.

*Cameron Dutro*

## 3.12.0

* Remove offline links from resources.
Expand Down
12 changes: 11 additions & 1 deletion lib/view_component/slot.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,17 @@ def to_s

if defined?(@__vc_content_block)
# render_in is faster than `parent.render`
@__vc_component_instance.render_in(view_context, &@__vc_content_block)
@__vc_component_instance.render_in(view_context) do |*args|
return @__vc_content_block.call(*args) if @__vc_content_block&.source_location.nil?

block_context = @__vc_content_block.binding.receiver

if block_context.class < ActionView::Base
block_context.capture(*args, &@__vc_content_block)
else
@__vc_content_block.call(*args)
end
end
else
@__vc_component_instance.render_in(view_context)
end
Expand Down
3 changes: 3 additions & 0 deletions performance/components/slots_wrapper_component.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<%= render(Performance::SlotsComponent.new(name: "Fox Mulder")) do |component| %>
<% component.with_header(classes: "foo") { "Header" } %>
<% end %>
4 changes: 4 additions & 0 deletions performance/components/slots_wrapper_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# frozen_string_literal: true

class Performance::SlotsWrapperComponent < ViewComponent::Base
end
32 changes: 32 additions & 0 deletions performance/slots_benchmark.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# frozen_string_literal: true

# Run `bundle exec rake benchmark` to execute benchmark.
# This is very much a work-in-progress. Please feel free to make/suggest improvements!

require "benchmark/ips"

# Configure Rails Environment
ENV["RAILS_ENV"] = "production"
require File.expand_path("../test/sandbox/config/environment.rb", __dir__)

module Performance
require_relative "components/slots_component"
require_relative "components/slots_wrapper_component"
end

class BenchmarksController < ActionController::Base
end

BenchmarksController.view_paths = [File.expand_path("./views", __dir__)]
controller_view = BenchmarksController.new.view_context

Benchmark.ips do |x|
x.time = 10
x.warmup = 2

x.report("slots") do
controller_view.render(Performance::SlotsWrapperComponent.new(name: "Fox Mulder"))
end

x.compare!
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<%= render(ForwardingSlotComponent.new) do |panel| %>
<% panel.with_target_content do |header| %>
Target content
<% end %>
<% end %>
38 changes: 38 additions & 0 deletions test/sandbox/app/components/forwarding_slot_wrapper_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# frozen_string_literal: true

class ForwardingSlotWrapperComponent < ViewComponent::Base
end

unless defined?(ForwardingSlotComponent)
class ForwardingSlotComponent < ViewComponent::Base
def initialize
@target = TargetSlotComponent.new
end

def with_target_content(&block)
@target.with_target_content(&block)
end

def before_render
content
end

def call
render(@target)
end
end

class TargetSlotComponent < ViewComponent::Base
renders_one :target_content, "TargetContentComponent"

def call
target_content
end
end

class TargetContentComponent < ViewComponent::Base
def call
content
end
end
end
6 changes: 6 additions & 0 deletions test/sandbox/test/slotable_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -758,4 +758,10 @@ def test_inline_html_escape_with_integer
render_inline InlineIntegerComponent.new
end
end

def test_forwarded_slot_renders_correctly
render_inline(ForwardingSlotWrapperComponent.new)

assert_text "Target content", count: 1
end
end

0 comments on commit 719c28a

Please sign in to comment.