view_component: Component previews that aren't declared with templates don't appear to support slots

Discussed in https://github.com/github/view_component/discussions/1321

I was able to replicate this issue raised by @muriloime. With these files in place (preview in test/components/previews/, the rest in app/components), navigate to http://localhost:3000/rails/view_components/outer_component/default.

You’d expect the preview to render as follows:

<div>
  <p>OuterComponent</p>
  <div>Add Inner template here</div>

</div>

But instead, it renders like this:

<div>
  <p>OuterComponent</p>
  
</div>

Presumably this is to say the content of the slot is ignored.

The interim fix for this is to use preview templates to render your component.

This does feel familiar to me, and could be a duplicate or something we’ve previously overlooked or not had time to fix.

<div type='discussions-op-text'>

Originally posted by muriloime March 29, 2022 Hi all, The components do not show when inside slots in a preview. Example:

outer_component.rb

class OuterComponent
 renders_one :button 
end 

outer_component preview

    render(OuterComponent.new) do |c|
      c.button { 'My awesome button' }
    end

This works as expected and documented in https://viewcomponent.org/guide/slots.html. However, when using a component:

    render(OuterComponent.new) do |c|
      c.button { render InnerComponent.new }
    end

does not work ( inner component does not render. I tried both with and without the inner render ) . Am I doing something stupid here or there is a bug?

many thanks

</div>

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 2
  • Comments: 15 (5 by maintainers)

Commits related to this issue

Most upvoted comments

@PedroAugustoRamalhoDuarte #render appends to the output buffer that your controller returns or your view renders. When you’re passing something to #with_content, it needs to be a string so that it can be interpolated by the template correctly. You may want to use #render_in instead here.

Thank you, you saved me a lot of effort haha. If anyone else stumbles upon this thread, rendering components in slots in previews is pretty easy:

class TableComponentPreview < ViewComponent::Preview
  def default
    render(TableComponent.new) do |c|
      c.with_head do
        TableHeadComponent.new(
          columns: ['First Name', 'Middle Name', 'Last Name'],
        )
        .render_in(c)
      end
    end
  end
end

Just saw this and throwing in a possible work around instead of using render_with_template

Im pretty sure you can call render on the outer component to ensure the inner component renders in its context, ie

render(OuterComponent.new) do |c|
  c.button { c.render InnerComponent.new }
end

At least thats what Im doing in my previews, eg

render ::NotificationComponent.new(title: title) do |notification|
  notification.avatar { notification.render ::AvatarComponent.new(...) }
end

Not to necro-post, but I want to add that the above solution worked equally well with the default content slot:

render(CardComponent.new) do |c|
  PlaceholderComponent.new.render_in(c)
end
SEO juice

Here’s the search terms I tried to use, which will hopefully help some other poor sap find this:

  • Rails ViewComponent Preview Nested Render

@PedroAugustoRamalhoDuarte #render appends to the output buffer that your controller returns or your view renders. When you’re passing something to #with_content, it needs to be a string so that it can be interpolated by the template correctly. You may want to use #render_in instead here.

HI @joelhawksley . Indeed, the problem exists in the controller context too.

I am still getting my head around the view_component code, but the branch https://github.com/muriloime/view_component/tree/fix_slot_rendering contains the two failing tests .

cheers Murilo