Skip to content

Commit

Permalink
Allow assigning a spacer component between items in a rendered collec…
Browse files Browse the repository at this point in the history
…tion (#2137)

* Allow rendering of spacer component between collection items

* Add changelog entry

* Keep in sync

* Update to accept component instance

* Apply suggestions from code review

---------

Co-authored-by: Joel Hawksley <[email protected]>
Co-authored-by: Joel Hawksley <[email protected]>
  • Loading branch information
3 people authored Oct 31, 2024
1 parent 4ff8327 commit 5b07cf1
Show file tree
Hide file tree
Showing 6 changed files with 44 additions and 5 deletions.
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

* Allow rendering `with_collection` to accept an optional `spacer_component` to be rendered between each item.

*Nick Coyne*

* Remove OpenStruct from codebase.

*Oleksii Vasyliev*
Expand Down
13 changes: 13 additions & 0 deletions docs/guide/collections.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,16 @@ class ProductComponent < ViewComponent::Base
end
end
```

## Spacer components

Since 3.20.0
{: .label }

Set `:spacer_component` as an instantiated component to render between items:

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

Which will render the SpacerComponent component between `ProductComponent`s.
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] Component instance to be rendered between items.
# @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
14 changes: 12 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,14 @@ def component_options(item, iterator)

@options.merge(item_options)
end

def rendered_spacer(view_context)
if @spacer_component
@spacer_component.set_original_view_context(__vc_original_view_context)
@spacer_component.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 = [Product.new(name: "Radio clock"), Product.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.new))
assert_selector("hr", count: 1)
end
end
end
2 changes: 1 addition & 1 deletion test/sandbox/test/rendering_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def test_render_inline_allocations
ViewComponent::CompileCache.cache.delete(MyComponent)
MyComponent.ensure_compiled

assert_allocations("3.4.0" => 110, "3.3.5" => 116, "3.3.0" => 129, "3.2.5" => 115, "3.1.6" => 115, "3.0.7" => 125) do
assert_allocations("3.4.0" => 110, "3.3.5" => 116, "3.3.0" => 129, "3.2.6" => 115, "3.1.6" => 115, "3.0.7" => 125) do
render_inline(MyComponent.new)
end

Expand Down

0 comments on commit 5b07cf1

Please sign in to comment.