Skip to content

Commit

Permalink
Allow rendering of spacer component between collection items
Browse files Browse the repository at this point in the history
  • Loading branch information
nickcoyne committed Oct 16, 2024
1 parent 23ef08b commit 643dc12
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 4 deletions.
11 changes: 11 additions & 0 deletions docs/guide/collections.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,14 @@ class ProductComponent < ViewComponent::Base
end
end
```

## Spacer Components

You can also specify a component to be rendered between instances of the main component by using the :spacer_component option:

```erb
<%= render(ProductComponent.with_collection(@products, spacer_component: SpacerComponent)) %>
```

ViewComponent will render the SpacerComponent component between each pair of ProductComponent components.
5 changes: 3 additions & 2 deletions lib/view_component/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -509,9 +509,10 @@ def sidecar_files(extensions)
# ```
#
# @param collection [Enumerable] A list of items to pass the ViewComponent one at a time.
# @param spacer_component [ViewComponent::Base] A spacer component to be rendered.
# @param args [Arguments] Arguments to pass to the ViewComponent every time.
def with_collection(collection, **args)
Collection.new(self, collection, **args)
def with_collection(collection, spacer_component: nil, **args)
Collection.new(self, collection, spacer_component, **args)
end

# @private
Expand Down
15 changes: 13 additions & 2 deletions lib/view_component/collection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def render_in(view_context, &block)
components.map do |component|
component.set_original_view_context(__vc_original_view_context)
component.render_in(view_context, &block)
end.join.html_safe
end.join(rendered_spacer(view_context)).html_safe
end

def components
Expand Down Expand Up @@ -48,9 +48,10 @@ def format

private

def initialize(component, object, **options)
def initialize(component, object, spacer_component, **options)
@component = component
@collection = collection_variable(object || [])
@spacer_component = spacer_component
@options = options
end

Expand All @@ -69,5 +70,15 @@ def component_options(item, iterator)

@options.merge(item_options)
end

def rendered_spacer(view_context)
if @spacer_component
spacer = @spacer_component.new
spacer.set_original_view_context(__vc_original_view_context)
spacer.render_in(view_context)
else
""
end
end
end
end
11 changes: 11 additions & 0 deletions test/sandbox/test/collection_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ def call
end
end

class SpacerComponent < ViewComponent::Base
def call
"<hr>".html_safe
end
end

def setup
@products = [OpenStruct.new(name: "Radio clock"), OpenStruct.new(name: "Mints")]
@collection = ProductComponent.with_collection(@products, notice: "secondhand")
Expand All @@ -35,5 +41,10 @@ def test_supports_components_with_keyword_args
assert_selector("*[data-name='#{@products.first.name}']", text: @products.first.name)
assert_selector("*[data-name='#{@products.last.name}']", text: @products.last.name)
end

def test_supports_collection_with_spacer_component
render_inline(ProductComponent.with_collection(@products, spacer_component: SpacerComponent))
assert_selector("hr", count: 1)
end
end
end

0 comments on commit 643dc12

Please sign in to comment.