Skip to content

Commit 5b07cf1

Browse files
Allow assigning a spacer component between items in a rendered collection (#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]>
1 parent 4ff8327 commit 5b07cf1

File tree

6 files changed

+44
-5
lines changed

6 files changed

+44
-5
lines changed

docs/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ nav_order: 5
1010

1111
## main
1212

13+
* Allow rendering `with_collection` to accept an optional `spacer_component` to be rendered between each item.
14+
15+
*Nick Coyne*
16+
1317
* Remove OpenStruct from codebase.
1418

1519
*Oleksii Vasyliev*

docs/guide/collections.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,3 +110,16 @@ class ProductComponent < ViewComponent::Base
110110
end
111111
end
112112
```
113+
114+
## Spacer components
115+
116+
Since 3.20.0
117+
{: .label }
118+
119+
Set `:spacer_component` as an instantiated component to render between items:
120+
121+
```erb
122+
<%= render(ProductComponent.with_collection(@products, spacer_component: SpacerComponent.new)) %>
123+
```
124+
125+
Which will render the SpacerComponent component between `ProductComponent`s.

lib/view_component/base.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -509,9 +509,10 @@ def sidecar_files(extensions)
509509
# ```
510510
#
511511
# @param collection [Enumerable] A list of items to pass the ViewComponent one at a time.
512+
# @param spacer_component [ViewComponent::Base] Component instance to be rendered between items.
512513
# @param args [Arguments] Arguments to pass to the ViewComponent every time.
513-
def with_collection(collection, **args)
514-
Collection.new(self, collection, **args)
514+
def with_collection(collection, spacer_component: nil, **args)
515+
Collection.new(self, collection, spacer_component, **args)
515516
end
516517

517518
# @private

lib/view_component/collection.rb

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def render_in(view_context, &block)
1919
components.map do |component|
2020
component.set_original_view_context(__vc_original_view_context)
2121
component.render_in(view_context, &block)
22-
end.join.html_safe
22+
end.join(rendered_spacer(view_context)).html_safe
2323
end
2424

2525
def components
@@ -48,9 +48,10 @@ def format
4848

4949
private
5050

51-
def initialize(component, object, **options)
51+
def initialize(component, object, spacer_component, **options)
5252
@component = component
5353
@collection = collection_variable(object || [])
54+
@spacer_component = spacer_component
5455
@options = options
5556
end
5657

@@ -69,5 +70,14 @@ def component_options(item, iterator)
6970

7071
@options.merge(item_options)
7172
end
73+
74+
def rendered_spacer(view_context)
75+
if @spacer_component
76+
@spacer_component.set_original_view_context(__vc_original_view_context)
77+
@spacer_component.render_in(view_context)
78+
else
79+
""
80+
end
81+
end
7282
end
7383
end

test/sandbox/test/collection_test.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ def call
1616
end
1717
end
1818

19+
class SpacerComponent < ViewComponent::Base
20+
def call
21+
"<hr>".html_safe
22+
end
23+
end
24+
1925
def setup
2026
@products = [Product.new(name: "Radio clock"), Product.new(name: "Mints")]
2127
@collection = ProductComponent.with_collection(@products, notice: "secondhand")
@@ -35,5 +41,10 @@ def test_supports_components_with_keyword_args
3541
assert_selector("*[data-name='#{@products.first.name}']", text: @products.first.name)
3642
assert_selector("*[data-name='#{@products.last.name}']", text: @products.last.name)
3743
end
44+
45+
def test_supports_collection_with_spacer_component
46+
render_inline(ProductComponent.with_collection(@products, spacer_component: SpacerComponent.new))
47+
assert_selector("hr", count: 1)
48+
end
3849
end
3950
end

test/sandbox/test/rendering_test.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def test_render_inline_allocations
1515
ViewComponent::CompileCache.cache.delete(MyComponent)
1616
MyComponent.ensure_compiled
1717

18-
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
18+
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
1919
render_inline(MyComponent.new)
2020
end
2121

0 commit comments

Comments
 (0)