From 643dc12c613149d2cc34580553ecc617d63dd009 Mon Sep 17 00:00:00 2001 From: Nick Coyne Date: Thu, 17 Oct 2024 12:51:08 +1300 Subject: [PATCH 1/6] Allow rendering of spacer component between collection items --- docs/guide/collections.md | 11 +++++++++++ lib/view_component/base.rb | 5 +++-- lib/view_component/collection.rb | 15 +++++++++++++-- test/sandbox/test/collection_test.rb | 11 +++++++++++ 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/docs/guide/collections.md b/docs/guide/collections.md index 48ae219cf..0f97f5a80 100644 --- a/docs/guide/collections.md +++ b/docs/guide/collections.md @@ -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. diff --git a/lib/view_component/base.rb b/lib/view_component/base.rb index 8d9ecde03..7404045ed 100644 --- a/lib/view_component/base.rb +++ b/lib/view_component/base.rb @@ -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 diff --git a/lib/view_component/collection.rb b/lib/view_component/collection.rb index e4082dc0d..378529abe 100644 --- a/lib/view_component/collection.rb +++ b/lib/view_component/collection.rb @@ -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 @@ -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 @@ -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 diff --git a/test/sandbox/test/collection_test.rb b/test/sandbox/test/collection_test.rb index bad2b0aa2..6cac666f1 100644 --- a/test/sandbox/test/collection_test.rb +++ b/test/sandbox/test/collection_test.rb @@ -16,6 +16,12 @@ def call end end + class SpacerComponent < ViewComponent::Base + def call + "
".html_safe + end + end + def setup @products = [OpenStruct.new(name: "Radio clock"), OpenStruct.new(name: "Mints")] @collection = ProductComponent.with_collection(@products, notice: "secondhand") @@ -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 From a721c5edc93a31744fefe567ee1642eee2d629a4 Mon Sep 17 00:00:00 2001 From: Nick Coyne Date: Thu, 17 Oct 2024 12:56:29 +1300 Subject: [PATCH 2/6] Add changelog entry --- docs/CHANGELOG.md | 4 ++++ docs/guide/collections.md | 3 +++ 2 files changed, 7 insertions(+) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 2e3ff1d6b..4c2bd0441 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -34,6 +34,10 @@ nav_order: 5 *Javier Aranda* +* Allow rendering `with_collection` to accept an optional `spacer_component` to be rendered between each item. + + *Nick Coyne* + ## 3.17.0 * Use struct instead openstruct in lib code. diff --git a/docs/guide/collections.md b/docs/guide/collections.md index 0f97f5a80..3f823de6a 100644 --- a/docs/guide/collections.md +++ b/docs/guide/collections.md @@ -113,6 +113,9 @@ end ## Spacer Components +Since 3.18.0 +{: .label } + You can also specify a component to be rendered between instances of the main component by using the :spacer_component option: ```erb From 0e902c3f683911c4239e151ebeeebce73651f10a Mon Sep 17 00:00:00 2001 From: Nick Coyne Date: Thu, 24 Oct 2024 13:15:05 +1300 Subject: [PATCH 3/6] Keep in sync --- docs/CHANGELOG.md | 8 ++++---- docs/guide/collections.md | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index d9585b89a..12b3622cf 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -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* + ## 3.19.0 * Relax Active Support version constraint in gemspec. @@ -42,10 +46,6 @@ nav_order: 5 *Javier Aranda* -* Allow rendering `with_collection` to accept an optional `spacer_component` to be rendered between each item. - - *Nick Coyne* - ## 3.17.0 * Use struct instead openstruct in lib code. diff --git a/docs/guide/collections.md b/docs/guide/collections.md index 3f823de6a..db35bf3db 100644 --- a/docs/guide/collections.md +++ b/docs/guide/collections.md @@ -113,7 +113,7 @@ end ## Spacer Components -Since 3.18.0 +Since 3.20.0 {: .label } You can also specify a component to be rendered between instances of the main component by using the :spacer_component option: From ce555913f83f11547fcb74c06af04cd416800cf7 Mon Sep 17 00:00:00 2001 From: Nick Coyne Date: Thu, 31 Oct 2024 12:59:07 +1300 Subject: [PATCH 4/6] Update to accept component instance --- docs/guide/collections.md | 4 ++-- lib/view_component/base.rb | 2 +- lib/view_component/collection.rb | 5 ++--- test/sandbox/test/collection_test.rb | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/guide/collections.md b/docs/guide/collections.md index db35bf3db..001ef5159 100644 --- a/docs/guide/collections.md +++ b/docs/guide/collections.md @@ -116,10 +116,10 @@ end Since 3.20.0 {: .label } -You can also specify a component to be rendered between instances of the main component by using the :spacer_component option: +You can also specify a component instance to be rendered between instances of the main component by using the :spacer_component option: ```erb -<%= render(ProductComponent.with_collection(@products, spacer_component: SpacerComponent)) %> +<%= render(ProductComponent.with_collection(@products, spacer_component: SpacerComponent.new)) %> ``` diff --git a/lib/view_component/base.rb b/lib/view_component/base.rb index 7404045ed..9976d5d38 100644 --- a/lib/view_component/base.rb +++ b/lib/view_component/base.rb @@ -509,7 +509,7 @@ 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 spacer_component [ViewComponent::Base] A spacer component instance to be rendered. # @param args [Arguments] Arguments to pass to the ViewComponent every time. def with_collection(collection, spacer_component: nil, **args) Collection.new(self, collection, spacer_component, **args) diff --git a/lib/view_component/collection.rb b/lib/view_component/collection.rb index 378529abe..798e38c25 100644 --- a/lib/view_component/collection.rb +++ b/lib/view_component/collection.rb @@ -73,9 +73,8 @@ def component_options(item, iterator) 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) + @spacer_component.set_original_view_context(__vc_original_view_context) + @spacer_component.render_in(view_context) else "" end diff --git a/test/sandbox/test/collection_test.rb b/test/sandbox/test/collection_test.rb index 6cac666f1..80355e415 100644 --- a/test/sandbox/test/collection_test.rb +++ b/test/sandbox/test/collection_test.rb @@ -43,7 +43,7 @@ def test_supports_components_with_keyword_args end def test_supports_collection_with_spacer_component - render_inline(ProductComponent.with_collection(@products, spacer_component: SpacerComponent)) + render_inline(ProductComponent.with_collection(@products, spacer_component: SpacerComponent.new)) assert_selector("hr", count: 1) end end From ea91a1e3cf61971a65b96f2b364eeb3f59932c21 Mon Sep 17 00:00:00 2001 From: Joel Hawksley Date: Thu, 31 Oct 2024 10:24:09 -0600 Subject: [PATCH 5/6] Apply suggestions from code review --- docs/guide/collections.md | 7 +++---- lib/view_component/base.rb | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/guide/collections.md b/docs/guide/collections.md index 001ef5159..91ad90e3c 100644 --- a/docs/guide/collections.md +++ b/docs/guide/collections.md @@ -111,16 +111,15 @@ class ProductComponent < ViewComponent::Base end ``` -## Spacer Components +## Spacer components Since 3.20.0 {: .label } -You can also specify a component instance to be rendered between instances of the main component by using the :spacer_component option: +Set `:spacer_component` as an instantiated component to render between items: ```erb <%= render(ProductComponent.with_collection(@products, spacer_component: SpacerComponent.new)) %> - ``` -ViewComponent will render the SpacerComponent component between each pair of ProductComponent components. +Which will render the SpacerComponent component between `ProductComponent`s. diff --git a/lib/view_component/base.rb b/lib/view_component/base.rb index 9976d5d38..958f057fd 100644 --- a/lib/view_component/base.rb +++ b/lib/view_component/base.rb @@ -509,7 +509,7 @@ 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 instance to be rendered. + # @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, spacer_component: nil, **args) Collection.new(self, collection, spacer_component, **args) From 633485148b1a286732c603d39ad359e31036c39d Mon Sep 17 00:00:00 2001 From: Joel Hawksley Date: Thu, 31 Oct 2024 10:48:17 -0600 Subject: [PATCH 6/6] I guess we're using 3.2.6 now? --- test/sandbox/test/rendering_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/sandbox/test/rendering_test.rb b/test/sandbox/test/rendering_test.rb index 6f22d551a..80fd7b924 100644 --- a/test/sandbox/test/rendering_test.rb +++ b/test/sandbox/test/rendering_test.rb @@ -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