Repository: ViewComponent/view_component (Ruby)

Issue:
view_component raises an error when a slot capture block returns nil (using Slim)
<!-- If possible, please provide a PR with a failing test for bugs instead of filing an issue. -->

### Steps to reproduce
Run the newly generated rails app here: https://github.com/yjukaku/view_component_nil_error and open localhost:3000/test

For reference, here's an example of code that triggers this issue:

```
# test_component.rb
class TestComponent < ViewComponent::Base
  renders_one :my_slot
end

# test_component.html.slim
| Hello!
= my_slot

# test.html.slim
= render TestComponent.new do |c|
  = c.my_slot do
    - something_that_returns_nil
```

### Expected behavior
<!-- Tell us what should happen -->
The component should render, with no content in the slot.

### Actual behavior
An error is raised: `no implicit conversion of nil into String`

```
temple (0.8.2) lib/temple/utils.rb:38:in `escapeHTML'
temple (0.8.2) lib/temple/utils.rb:38:in `escape_html'
temple (0.8.2) lib/temple/utils.rb:21:in `escape_html_safe'
app/views/test/test.html.slim:4
actionview (6.1.4.1) lib/action_view/helpers/capture_helper.rb:45:in `block in capture'
actionview (6.1.4.1) lib/action_view/helpers/capture_helper.rb:209:in `with_output_buffer'
actionview (6.1.4.1) lib/action_view/helpers/capture_helper.rb:45:in `capture'
view_component (2.38.0) lib/view_component/base.rb:247:in `content'
view_component (2.38.0) lib/view_component/slotable_v2.rb:222:in `get_slot'
view_component (2.38.0) lib/view_component/slotable_v2.rb:72:in `block in renders_one'
app/components/test_component.html.slim:3:in `call'
view_component (2.38.0) lib/view_component/compiler.rb:83:in `render_template_for'
view_component (2.38.0) lib/view_component/base.rb:102:in `render_in'
actionview (6.1.4.1) lib/action_view/helpers/rendering_helper.rb:43:in `render'
app/views/test/test.html.slim:1
actionview (6.1.4.1) lib/action_view/base.rb:247:in `public_send'
```

**Please note: The line number in the exception trace corresponding to `test.html.slim file` is off by one, for some reason, so keep that in mind while debugging.**

### System configuration
**Rails version**:
Rails 6.1.4.1

**Ruby version**:
ruby 3.0.2p107 (2021-07-07 revision 0db68f0233) [x86_64-darwin19]

**Gem version**:
view_component-2.38.0

---

The example above is a bit contrived, but here's a more likely example:

```
# test.html.slim
= render TestComponent.new do |c|
  = c.my_slot
    - if user_signed_in?
       | Welcome!
```

In that case, the `if` condition evaluating to false implies a `nil` return.


Discussion:
After digging in to the VC code a bit, I think forcing `ViewComponent::SlotV2` to call `to_s` on whatever is returned from the capture block would solve this issue. 

I would guess the issue is that https://github.com/github/view_component/blob/main/lib/view_component/slot_v2.rb#L62 is returning `nil`. Calling `.to_s` on that return value should work to solve our issue, IMO.
@yjukaku that sounds like a good idea. Would you like to open a PR?
@joelhawksley , yep! Here you go: https://github.com/github/view_component/pull/1046



Patch:
diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
index c19f59070..986ffdc9e 100644
--- a/docs/CHANGELOG.md
+++ b/docs/CHANGELOG.md
@@ -15,6 +15,10 @@ title: Changelog
 
     *Nicolas Brousse*
 
+* Fix bug when using Slim and writing a slot whose block evaluates to `nil`.
+
+    *Yousuf Jukaku*
+
 ## 2.39.0
 
 * Clarify documentation of `with_variant` as an override of Action Pack.
diff --git a/lib/view_component/slot_v2.rb b/lib/view_component/slot_v2.rb
index 4ae56cee8..278b73b05 100644
--- a/lib/view_component/slot_v2.rb
+++ b/lib/view_component/slot_v2.rb
@@ -64,7 +64,7 @@ def to_s
           @__vc_content_set_by_with_content
              end
 
-      @content
+      @content = @content.to_s
     end
 
     # Allow access to public component methods via the wrapper


Test patch:
diff --git a/test/sandbox/app/components/empty_slot_v2_with_slim_component.html.slim b/test/sandbox/app/components/empty_slot_v2_with_slim_component.html.slim
new file mode 100644
index 000000000..76d18946c
--- /dev/null
+++ b/test/sandbox/app/components/empty_slot_v2_with_slim_component.html.slim
@@ -0,0 +1,3 @@
+span
+  ' Hello
+  = title
diff --git a/test/sandbox/app/components/empty_slot_v2_with_slim_component.rb b/test/sandbox/app/components/empty_slot_v2_with_slim_component.rb
new file mode 100644
index 000000000..24075aa9d
--- /dev/null
+++ b/test/sandbox/app/components/empty_slot_v2_with_slim_component.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+class EmptySlotV2WithSlimComponent < ViewComponent::Base
+  renders_one :title
+end
diff --git a/test/sandbox/app/views/integration_examples/empty_slot_v2_with_slim.html.slim b/test/sandbox/app/views/integration_examples/empty_slot_v2_with_slim.html.slim
new file mode 100644
index 000000000..4c7f3c67e
--- /dev/null
+++ b/test/sandbox/app/views/integration_examples/empty_slot_v2_with_slim.html.slim
@@ -0,0 +1,3 @@
+= render(EmptySlotV2WithSlimComponent.new) do |component|
+  - component.title
+    - nil
diff --git a/test/sandbox/config/routes.rb b/test/sandbox/config/routes.rb
index 8396e850a..d43590bd0 100644
--- a/test/sandbox/config/routes.rb
+++ b/test/sandbox/config/routes.rb
@@ -5,6 +5,7 @@
   get :content_areas, to: "integration_examples#content_areas"
   get :slots, to: "integration_examples#slots"
   get :empty_slot, to: "integration_examples#empty_slot"
+  get :empty_slot_v2_with_slim, to: "integration_examples#empty_slot_v2_with_slim"
   get :partial, to: "integration_examples#partial"
   get :content, to: "integration_examples#content"
   get :variants, to: "integration_examples#variants"
diff --git a/test/view_component/integration_test.rb b/test/view_component/integration_test.rb
index 4b304fb10..294d5dc67 100644
--- a/test/view_component/integration_test.rb
+++ b/test/view_component/integration_test.rb
@@ -407,6 +407,12 @@ def test_renders_empty_slot_without_error
     assert_response :success
   end
 
+  def test_renders_empty_slot_v2_with_slim_without_error
+    get "/empty_slot_v2_with_slim"
+
+    assert_response :success
+  end
+
   if Rails.version.to_f >= 6.1
     def test_rendering_component_using_the_render_component_helper_raises_an_error
       error =