view_component 2.57.1 → 2.60.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of view_component might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/app/controllers/concerns/view_component/preview_actions.rb +7 -4
- data/app/helpers/preview_helper.rb +3 -3
- data/docs/CHANGELOG.md +102 -0
- data/lib/rails/generators/abstract_generator.rb +4 -4
- data/lib/view_component/base.rb +65 -19
- data/lib/view_component/capybara_simple_session.rb +132 -0
- data/lib/view_component/collection.rb +3 -3
- data/lib/view_component/compiler.rb +23 -9
- data/lib/view_component/content_areas.rb +1 -1
- data/lib/view_component/docs_builder_component.rb +1 -1
- data/lib/view_component/engine.rb +6 -5
- data/lib/view_component/global_output_buffer.rb +4 -3
- data/lib/view_component/output_buffer_stack.rb +0 -2
- data/lib/view_component/polymorphic_slots.rb +17 -2
- data/lib/view_component/preview.rb +8 -6
- data/lib/view_component/render_component_to_string_helper.rb +1 -1
- data/lib/view_component/render_preview_helper.rb +11 -1
- data/lib/view_component/render_to_string_monkey_patch.rb +1 -1
- data/lib/view_component/rendering_component_helper.rb +1 -1
- data/lib/view_component/rendering_monkey_patch.rb +1 -1
- data/lib/view_component/slotable.rb +5 -6
- data/lib/view_component/slotable_v2.rb +7 -9
- data/lib/view_component/test_helpers.rb +28 -1
- data/lib/view_component/translatable.rb +9 -10
- data/lib/view_component/version.rb +2 -2
- data/lib/view_component.rb +1 -0
- metadata +6 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b74cdfd2d8306224e0fc89d244d70f08fbf779ec9a892affd0fe766b16a4ac6d
|
4
|
+
data.tar.gz: d401c34f06238713ba43d97e5b50b9db647f946f8937e964c57578918b3418fb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a3c56fb2399e387fb57f6976264ea7aa3d3ce9e4490a741cf660876fe90fb5265bf80a865887a082cf282259a04b8477416546bdd21a2be08ac21744aa8316b5
|
7
|
+
data.tar.gz: 71049fb1fcc3cd1a09404feb9ab7a5aee6b2c9e5dc88c377233472953ee3e498f4a95a973033d717413e8d128a0ea3e95d955885692581b9e62c26fb66a18d0f
|
@@ -37,21 +37,24 @@ module ViewComponent
|
|
37
37
|
opts = {}
|
38
38
|
opts[:layout] = layout if layout.present? || layout == false
|
39
39
|
opts[:locals] = locals if locals.present?
|
40
|
-
render "view_components/preview", opts
|
40
|
+
render "view_components/preview", opts
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
44
|
private
|
45
45
|
|
46
|
-
|
46
|
+
# :doc:
|
47
|
+
def default_preview_layout
|
47
48
|
ViewComponent::Base.default_preview_layout
|
48
49
|
end
|
49
50
|
|
50
|
-
|
51
|
+
# :doc:
|
52
|
+
def show_previews?
|
51
53
|
ViewComponent::Base.show_previews
|
52
54
|
end
|
53
55
|
|
54
|
-
|
56
|
+
# :doc:
|
57
|
+
def find_preview
|
55
58
|
candidates = []
|
56
59
|
params[:path].to_s.scan(%r{/|$}) { candidates << $` }
|
57
60
|
preview = candidates.detect { |candidate| ViewComponent::Preview.exists?(candidate) }
|
@@ -7,14 +7,14 @@ module PreviewHelper
|
|
7
7
|
def preview_source
|
8
8
|
return if @render_args.nil?
|
9
9
|
|
10
|
-
render "preview_source"
|
10
|
+
render "preview_source"
|
11
11
|
end
|
12
12
|
|
13
13
|
def find_template_data(lookup_context:, template_identifier:)
|
14
14
|
template = lookup_context.find_template(template_identifier)
|
15
15
|
|
16
16
|
if Rails.version.to_f >= 6.1 || template.source.present?
|
17
|
-
|
17
|
+
{
|
18
18
|
source: template.source,
|
19
19
|
prism_language_name: prism_language_name_by_template(template: template)
|
20
20
|
}
|
@@ -40,7 +40,7 @@ module PreviewHelper
|
|
40
40
|
template_source = File.read(template_file_path)
|
41
41
|
prism_language_name = prism_language_name_by_template_path(template_file_path: template_file_path)
|
42
42
|
|
43
|
-
|
43
|
+
{
|
44
44
|
source: template_source,
|
45
45
|
prism_language_name: prism_language_name
|
46
46
|
}
|
data/docs/CHANGELOG.md
CHANGED
@@ -9,6 +9,108 @@ title: Changelog
|
|
9
9
|
|
10
10
|
## main
|
11
11
|
|
12
|
+
## 2.60.0
|
13
|
+
|
14
|
+
* Add support for `render_preview` in RSpec tests.
|
15
|
+
|
16
|
+
*Thomas Hutterer*
|
17
|
+
|
18
|
+
## 2.59.0
|
19
|
+
|
20
|
+
* Expose Capybara DSL methods directly inside tests.
|
21
|
+
|
22
|
+
The following Capybara methods are now available directly without having to use the `page` method:
|
23
|
+
|
24
|
+
* [`all`](https://rubydoc.info/github/teamcapybara/capybara/Capybara%2FNode%2FFinders:all)
|
25
|
+
* [`first`](https://rubydoc.info/github/teamcapybara/capybara/Capybara%2FNode%2FFinders:first)
|
26
|
+
* [`text`](https://rubydoc.info/github/teamcapybara/capybara/Capybara%2FNode%2FSimple:text)
|
27
|
+
* [`find`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FFinders:find)
|
28
|
+
* [`find_all`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FFinders:find_all)
|
29
|
+
* [`find_button`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FFinders:find_button)
|
30
|
+
* [`find_by_id`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FFinders:find_by_id)
|
31
|
+
* [`find_field`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FFinders:find_field)
|
32
|
+
* [`find_link`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FFinders:find_link)
|
33
|
+
* [`has_content?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_content%3F)
|
34
|
+
* [`has_text?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_text%3F)
|
35
|
+
* [`has_css?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_css%3F)
|
36
|
+
* [`has_no_content?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_no_content%3F)
|
37
|
+
* [`has_no_text?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_no_text%3F)
|
38
|
+
* [`has_no_css?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_no_css%3F)
|
39
|
+
* [`has_no_xpath?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_no_xpath%3F)
|
40
|
+
* [`has_xpath?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_xpath%3F)
|
41
|
+
* [`has_link?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_link%3F)
|
42
|
+
* [`has_no_link?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_no_link%3F)
|
43
|
+
* [`has_button?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_button%3F)
|
44
|
+
* [`has_no_button?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_no_button%3F)
|
45
|
+
* [`has_field?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_field%3F)
|
46
|
+
* [`has_no_field?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_no_field%3F)
|
47
|
+
* [`has_checked_field?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_checked_field%3F)
|
48
|
+
* [`has_unchecked_field?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_unchecked_field%3F)
|
49
|
+
* [`has_no_table?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_no_table%3F)
|
50
|
+
* [`has_table?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_table%3F)
|
51
|
+
* [`has_select?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_select%3F)
|
52
|
+
* [`has_no_select?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_no_select%3F)
|
53
|
+
* [`has_selector?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_selector%3F)
|
54
|
+
* [`has_no_selector?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_no_selector%3F)
|
55
|
+
* [`has_no_checked_field?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_no_checked_field%3F)
|
56
|
+
* [`has_no_unchecked_field?`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FNode%2FMatchers:has_no_unchecked_field%3F)
|
57
|
+
|
58
|
+
* Add support for `within*` Capybara DLS methods:
|
59
|
+
|
60
|
+
* [`within`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FSession:within)
|
61
|
+
* [`within_element`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FSession:within)
|
62
|
+
* [`within_fieldset`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FSession:within_fieldset)
|
63
|
+
* [`within_table`](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara%2FSession:within_table)
|
64
|
+
|
65
|
+
*Jacob Carlborg*
|
66
|
+
|
67
|
+
## 2.58.0
|
68
|
+
|
69
|
+
* Switch to `standardrb`.
|
70
|
+
|
71
|
+
*Joel Hawksley*
|
72
|
+
|
73
|
+
* Add BootrAils article to resources.
|
74
|
+
|
75
|
+
*Joel Hawksley*
|
76
|
+
|
77
|
+
* Add @boardfish and @spone as maintainers.
|
78
|
+
|
79
|
+
*Joel Hawksley*, *Cameron Dutro*, *Blake Williams*
|
80
|
+
|
81
|
+
* Re-compile updated, inherited templates when class caching is disabled.
|
82
|
+
|
83
|
+
*Patrick Arnett*
|
84
|
+
|
85
|
+
* Add the latest version to the docs index.
|
86
|
+
* Improve the docs: add the versions various features were introduced in.
|
87
|
+
|
88
|
+
*Hans Lemuet*
|
89
|
+
|
90
|
+
* Update docs to reflect lack of block content support in controllers.
|
91
|
+
|
92
|
+
*Joel Hawksley*
|
93
|
+
|
94
|
+
* Prevent adding duplicates to `autoload_paths`.
|
95
|
+
|
96
|
+
*Thomas Hutterer*
|
97
|
+
|
98
|
+
* Add FreeAgent to list of companies using ViewComponent.
|
99
|
+
|
100
|
+
*Simon Fish*
|
101
|
+
|
102
|
+
* Include polymorphic slots in `ViewComponent::Base` by default.
|
103
|
+
|
104
|
+
*Cameron Dutro*
|
105
|
+
|
106
|
+
* Add per-component config option for stripping newlines from templates before compilation.
|
107
|
+
|
108
|
+
*Cameron Dutro*
|
109
|
+
|
110
|
+
* Add link to article by Matouš Borák.
|
111
|
+
|
112
|
+
*Joel Hawksley*
|
113
|
+
|
12
114
|
## 2.57.1
|
13
115
|
|
14
116
|
* Fix issue causing `NoMethodError`s when calling helper methods from components rendered as part of a collection.
|
@@ -36,10 +36,10 @@ module ViewComponent
|
|
36
36
|
|
37
37
|
def stimulus_controller
|
38
38
|
if options["stimulus"]
|
39
|
-
File.join(destination_directory, destination_file_name)
|
40
|
-
sub("#{component_path}/", "")
|
41
|
-
|
42
|
-
gsub("/", "--")
|
39
|
+
File.join(destination_directory, destination_file_name)
|
40
|
+
.sub("#{component_path}/", "")
|
41
|
+
.tr("_", "-")
|
42
|
+
.gsub("/", "--")
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
data/lib/view_component/base.rb
CHANGED
@@ -16,6 +16,7 @@ module ViewComponent
|
|
16
16
|
class Base < ActionView::Base
|
17
17
|
include ActiveSupport::Configurable
|
18
18
|
include ViewComponent::ContentAreas
|
19
|
+
include ViewComponent::PolymorphicSlots
|
19
20
|
include ViewComponent::Previewable
|
20
21
|
include ViewComponent::SlotableV2
|
21
22
|
include ViewComponent::Translatable
|
@@ -31,6 +32,10 @@ module ViewComponent
|
|
31
32
|
class_attribute :content_areas
|
32
33
|
self.content_areas = [] # class_attribute:default doesn't work until Rails 5.2
|
33
34
|
|
35
|
+
# Config option that strips trailing whitespace in templates before compiling them.
|
36
|
+
class_attribute :__vc_strip_trailing_whitespace, instance_accessor: false, instance_predicate: false
|
37
|
+
self.__vc_strip_trailing_whitespace = false # class_attribute:default doesn't work until Rails 5.2
|
38
|
+
|
34
39
|
attr_accessor :__vc_original_view_context
|
35
40
|
|
36
41
|
# Components render in their own view context. Helpers and other functionality
|
@@ -135,8 +140,10 @@ module ViewComponent
|
|
135
140
|
# Subclass components that call `super` inside their template code will cause a
|
136
141
|
# double render if they emit the result:
|
137
142
|
#
|
138
|
-
#
|
139
|
-
#
|
143
|
+
# ```erb
|
144
|
+
# <%= super %> # double-renders
|
145
|
+
# <% super %> # does not double-render
|
146
|
+
# ```
|
140
147
|
#
|
141
148
|
# Calls `super`, returning `nil` to avoid rendering the result twice.
|
142
149
|
def render_parent
|
@@ -187,7 +194,8 @@ module ViewComponent
|
|
187
194
|
end
|
188
195
|
|
189
196
|
# @private
|
190
|
-
def initialize(*)
|
197
|
+
def initialize(*)
|
198
|
+
end
|
191
199
|
|
192
200
|
# Re-use original view_context if we're not rendering a component.
|
193
201
|
#
|
@@ -315,7 +323,9 @@ module ViewComponent
|
|
315
323
|
|
316
324
|
# Set the controller used for testing components:
|
317
325
|
#
|
318
|
-
#
|
326
|
+
# ```ruby
|
327
|
+
# config.view_component.test_controller = "MyTestController"
|
328
|
+
# ```
|
319
329
|
#
|
320
330
|
# Defaults to ApplicationController. Can also be configured on a per-test
|
321
331
|
# basis using `with_controller_class`.
|
@@ -325,13 +335,17 @@ module ViewComponent
|
|
325
335
|
|
326
336
|
# Set if render monkey patches should be included or not in Rails <6.1:
|
327
337
|
#
|
328
|
-
#
|
338
|
+
# ```ruby
|
339
|
+
# config.view_component.render_monkey_patch_enabled = false
|
340
|
+
# ```
|
329
341
|
#
|
330
342
|
mattr_accessor :render_monkey_patch_enabled, instance_writer: false, default: true
|
331
343
|
|
332
344
|
# Path for component files
|
333
345
|
#
|
334
|
-
#
|
346
|
+
# ```ruby
|
347
|
+
# config.view_component.view_component_path = "app/my_components"
|
348
|
+
# ```
|
335
349
|
#
|
336
350
|
# Defaults to `app/components`.
|
337
351
|
#
|
@@ -339,7 +353,9 @@ module ViewComponent
|
|
339
353
|
|
340
354
|
# Parent class for generated components
|
341
355
|
#
|
342
|
-
#
|
356
|
+
# ```ruby
|
357
|
+
# config.view_component.component_parent_class = "MyBaseComponent"
|
358
|
+
# ```
|
343
359
|
#
|
344
360
|
# Defaults to nil. If this is falsy, generators will use
|
345
361
|
# "ApplicationComponent" if defined, "ViewComponent::Base" otherwise.
|
@@ -355,25 +371,33 @@ module ViewComponent
|
|
355
371
|
#
|
356
372
|
# Always generate a component with a sidecar directory:
|
357
373
|
#
|
358
|
-
#
|
374
|
+
# ```ruby
|
375
|
+
# config.view_component.generate.sidecar = true
|
376
|
+
# ```
|
359
377
|
#
|
360
378
|
# #### #stimulus_controller
|
361
379
|
#
|
362
380
|
# Always generate a Stimulus controller alongside the component:
|
363
381
|
#
|
364
|
-
#
|
382
|
+
# ```ruby
|
383
|
+
# config.view_component.generate.stimulus_controller = true
|
384
|
+
# ```
|
365
385
|
#
|
366
386
|
# #### #locale
|
367
387
|
#
|
368
388
|
# Always generate translations file alongside the component:
|
369
389
|
#
|
370
|
-
#
|
390
|
+
# ```ruby
|
391
|
+
# config.view_component.generate.locale = true
|
392
|
+
# ```
|
371
393
|
#
|
372
394
|
# #### #distinct_locale_files
|
373
395
|
#
|
374
396
|
# Always generate as many translations files as available locales:
|
375
397
|
#
|
376
|
-
#
|
398
|
+
# ```ruby
|
399
|
+
# config.view_component.generate.distinct_locale_files = true
|
400
|
+
# ```
|
377
401
|
#
|
378
402
|
# One file will be generated for each configured `I18n.available_locales`,
|
379
403
|
# falling back to `[:en]` when no `available_locales` is defined.
|
@@ -382,7 +406,9 @@ module ViewComponent
|
|
382
406
|
#
|
383
407
|
# Always generate preview alongside the component:
|
384
408
|
#
|
385
|
-
#
|
409
|
+
# ```ruby
|
410
|
+
# config.view_component.generate.preview = true
|
411
|
+
# ```
|
386
412
|
#
|
387
413
|
# Defaults to `false`.
|
388
414
|
mattr_accessor :generate, instance_writer: false, default: ActiveSupport::OrderedOptions.new(false)
|
@@ -436,7 +462,9 @@ module ViewComponent
|
|
436
462
|
|
437
463
|
# Render a component for each element in a collection ([documentation](/guide/collections)):
|
438
464
|
#
|
439
|
-
#
|
465
|
+
# ```ruby
|
466
|
+
# render(ProductsComponent.with_collection(@products, foo: :bar))
|
467
|
+
# ```
|
440
468
|
#
|
441
469
|
# @param collection [Enumerable] A list of items to pass the ViewComponent one at a time.
|
442
470
|
# @param args [Arguments] Arguments to pass to the ViewComponent every time.
|
@@ -532,13 +560,35 @@ module ViewComponent
|
|
532
560
|
|
533
561
|
# Set the parameter name used when rendering elements of a collection ([documentation](/guide/collections)):
|
534
562
|
#
|
535
|
-
#
|
563
|
+
# ```ruby
|
564
|
+
# with_collection_parameter :item
|
565
|
+
# ```
|
536
566
|
#
|
537
567
|
# @param parameter [Symbol] The parameter name used when rendering elements of a collection.
|
538
568
|
def with_collection_parameter(parameter)
|
539
569
|
@provided_collection_parameter = parameter
|
540
570
|
end
|
541
571
|
|
572
|
+
# Strips trailing whitespace from templates before compiling them.
|
573
|
+
#
|
574
|
+
# ```ruby
|
575
|
+
# class MyComponent < ViewComponent::Base
|
576
|
+
# strip_trailing_whitespace
|
577
|
+
# end
|
578
|
+
# ```
|
579
|
+
#
|
580
|
+
# @param value [Boolean] Whether or not to strip newlines.
|
581
|
+
def strip_trailing_whitespace(value = true)
|
582
|
+
self.__vc_strip_trailing_whitespace = value
|
583
|
+
end
|
584
|
+
|
585
|
+
# Whether trailing whitespace will be stripped before compilation.
|
586
|
+
#
|
587
|
+
# @return [Boolean]
|
588
|
+
def strip_trailing_whitespace?
|
589
|
+
__vc_strip_trailing_whitespace
|
590
|
+
end
|
591
|
+
|
542
592
|
# Ensure the component initializer accepts the
|
543
593
|
# collection parameter. By default, we don't
|
544
594
|
# validate that the default parameter name
|
@@ -586,11 +636,7 @@ module ViewComponent
|
|
586
636
|
|
587
637
|
# @private
|
588
638
|
def collection_parameter
|
589
|
-
|
590
|
-
provided_collection_parameter
|
591
|
-
else
|
592
|
-
name && name.demodulize.underscore.chomp("_component").to_sym
|
593
|
-
end
|
639
|
+
provided_collection_parameter || name && name.demodulize.underscore.chomp("_component").to_sym
|
594
640
|
end
|
595
641
|
|
596
642
|
# @private
|
@@ -0,0 +1,132 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ViewComponent
|
4
|
+
# This is a simpler version of {Capybara::Session}.
|
5
|
+
#
|
6
|
+
# It only includes {Capybara::Node::Finders}, {Capybara::Node::Matchers},
|
7
|
+
# {#within} and {#within_element}. It is useful in that it does not require a
|
8
|
+
# session, an application or a driver, but can still use Capybara's finders
|
9
|
+
# and matchers on any string that contains HTML.
|
10
|
+
class CapybaraSimpleSession
|
11
|
+
# Most of the code in this class is shamelessly stolen from the
|
12
|
+
# {Capybara::Session} class in the Capybara gem
|
13
|
+
# (https://github.com/teamcapybara/capybara/blob/e704d00879fb1d1e1a0cc01e04c101bcd8af4a68/lib/capybara/session.rb#L38).
|
14
|
+
|
15
|
+
NODE_METHODS = %i[
|
16
|
+
all
|
17
|
+
first
|
18
|
+
text
|
19
|
+
|
20
|
+
find
|
21
|
+
find_all
|
22
|
+
find_button
|
23
|
+
find_by_id
|
24
|
+
find_field
|
25
|
+
find_link
|
26
|
+
|
27
|
+
has_content?
|
28
|
+
has_text?
|
29
|
+
has_css?
|
30
|
+
has_no_content?
|
31
|
+
has_no_text?
|
32
|
+
has_no_css?
|
33
|
+
has_no_xpath?
|
34
|
+
has_xpath?
|
35
|
+
has_link?
|
36
|
+
has_no_link?
|
37
|
+
has_button?
|
38
|
+
has_no_button?
|
39
|
+
has_field?
|
40
|
+
has_no_field?
|
41
|
+
has_checked_field?
|
42
|
+
has_unchecked_field?
|
43
|
+
has_no_table?
|
44
|
+
has_table?
|
45
|
+
has_select?
|
46
|
+
has_no_select?
|
47
|
+
has_selector?
|
48
|
+
has_no_selector?
|
49
|
+
has_no_checked_field?
|
50
|
+
has_no_unchecked_field?
|
51
|
+
|
52
|
+
assert_selector
|
53
|
+
assert_no_selector
|
54
|
+
assert_all_of_selectors
|
55
|
+
assert_none_of_selectors
|
56
|
+
assert_any_of_selectors
|
57
|
+
assert_text
|
58
|
+
assert_no_text
|
59
|
+
].freeze
|
60
|
+
|
61
|
+
private_constant :NODE_METHODS
|
62
|
+
|
63
|
+
SESSION_METHODS = %i[within within_element within_fieldset within_table].freeze
|
64
|
+
|
65
|
+
private_constant :SESSION_METHODS
|
66
|
+
|
67
|
+
DSL_METHODS = (NODE_METHODS + SESSION_METHODS).freeze
|
68
|
+
|
69
|
+
# Stolen from: https://github.com/teamcapybara/capybara/blob/e704d00879fb1d1e1a0cc01e04c101bcd8af4a68/lib/capybara/session.rb#L767-L774.
|
70
|
+
NODE_METHODS.each do |method|
|
71
|
+
if RUBY_VERSION >= "2.7"
|
72
|
+
class_eval <<~METHOD, __FILE__, __LINE__ + 1
|
73
|
+
def #{method}(...)
|
74
|
+
current_scope.#{method}(...)
|
75
|
+
end
|
76
|
+
METHOD
|
77
|
+
else
|
78
|
+
define_method method do |*args, &block|
|
79
|
+
current_scope.send(method, *args, &block)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Initializes the receiver with the given string of HTML.
|
85
|
+
#
|
86
|
+
# @param html [String] the HTML to create the session out of
|
87
|
+
def initialize(html)
|
88
|
+
@document = Capybara::Node::Simple.new(html)
|
89
|
+
end
|
90
|
+
|
91
|
+
# (see Capybara::Session#within)
|
92
|
+
def within(*args, **kw_args)
|
93
|
+
new_scope = args.first.respond_to?(:to_capybara_node) ? args.first.to_capybara_node : find(*args, **kw_args)
|
94
|
+
begin
|
95
|
+
scopes.push(new_scope)
|
96
|
+
yield if block_given?
|
97
|
+
ensure
|
98
|
+
scopes.pop
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# (see Capybara::Session#within_element)
|
103
|
+
alias_method :within_element, :within
|
104
|
+
|
105
|
+
# (see Capybara::Session#within_fieldset)
|
106
|
+
def within_fieldset(locator, &block)
|
107
|
+
within(:fieldset, locator, &block)
|
108
|
+
end
|
109
|
+
|
110
|
+
# (see Capybara::Session#within_table)
|
111
|
+
def within_table(locator, &block)
|
112
|
+
within(:table, locator, &block)
|
113
|
+
end
|
114
|
+
|
115
|
+
# (see Capybara::Node::Element#native)
|
116
|
+
def native
|
117
|
+
current_scope.native
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
attr_reader :document
|
123
|
+
|
124
|
+
def scopes
|
125
|
+
@scopes ||= [nil]
|
126
|
+
end
|
127
|
+
|
128
|
+
def current_scope
|
129
|
+
scopes.last.presence || document
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -18,9 +18,9 @@ module ViewComponent
|
|
18
18
|
|
19
19
|
def render_in(view_context, &block)
|
20
20
|
components.map do |component|
|
21
|
-
component.set_original_view_context(
|
21
|
+
component.set_original_view_context(__vc_original_view_context)
|
22
22
|
component.render_in(view_context, &block)
|
23
|
-
end.join.html_safe
|
23
|
+
end.join.html_safe
|
24
24
|
end
|
25
25
|
|
26
26
|
def components
|
@@ -61,7 +61,7 @@ module ViewComponent
|
|
61
61
|
end
|
62
62
|
|
63
63
|
def component_options(item, iterator)
|
64
|
-
item_options = {
|
64
|
+
item_options = {component.collection_parameter => item}
|
65
65
|
item_options[component.collection_counter_parameter] = iterator.index + 1 if component.counter_argument_present?
|
66
66
|
item_options[component.collection_iteration_parameter] = iterator.dup if component.iteration_argument_present?
|
67
67
|
|
@@ -31,6 +31,8 @@ module ViewComponent
|
|
31
31
|
return if compiled? && !force
|
32
32
|
return if component_class == ViewComponent::Base
|
33
33
|
|
34
|
+
component_class.superclass.compile(raise_errors: raise_errors) if should_compile_superclass?
|
35
|
+
|
34
36
|
with_lock do
|
35
37
|
subclass_instance_methods = component_class.instance_methods(false)
|
36
38
|
|
@@ -68,11 +70,13 @@ module ViewComponent
|
|
68
70
|
component_class.send(:remove_method, method_name.to_sym)
|
69
71
|
end
|
70
72
|
|
73
|
+
# rubocop:disable Style/EvalWithLocation
|
71
74
|
component_class.class_eval <<-RUBY, template[:path], 0
|
72
75
|
def #{method_name}
|
73
76
|
#{compiled_template(template[:path])}
|
74
77
|
end
|
75
78
|
RUBY
|
79
|
+
# rubocop:enable Style/EvalWithLocation
|
76
80
|
end
|
77
81
|
|
78
82
|
define_render_template_for
|
@@ -151,15 +155,15 @@ module ViewComponent
|
|
151
155
|
end
|
152
156
|
|
153
157
|
invalid_variants =
|
154
|
-
templates
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
158
|
+
templates
|
159
|
+
.group_by { |template| template[:variant] }
|
160
|
+
.map { |variant, grouped| variant if grouped.length > 1 }
|
161
|
+
.compact
|
162
|
+
.sort
|
159
163
|
|
160
164
|
unless invalid_variants.empty?
|
161
165
|
errors <<
|
162
|
-
"More than one template found for #{
|
166
|
+
"More than one template found for #{"variant".pluralize(invalid_variants.count)} " \
|
163
167
|
"#{invalid_variants.map { |v| "'#{v}'" }.to_sentence} in #{component_class}. " \
|
164
168
|
"There can only be one template file per variant."
|
165
169
|
end
|
@@ -177,8 +181,8 @@ module ViewComponent
|
|
177
181
|
count = duplicate_template_file_and_inline_variant_calls.count
|
178
182
|
|
179
183
|
errors <<
|
180
|
-
"Template #{
|
181
|
-
"found for #{
|
184
|
+
"Template #{"file".pluralize(count)} and inline render #{"method".pluralize(count)} " \
|
185
|
+
"found for #{"variant".pluralize(count)} " \
|
182
186
|
"#{duplicate_template_file_and_inline_variant_calls.map { |v| "'#{v}'" }.to_sentence} " \
|
183
187
|
"in #{component_class}. " \
|
184
188
|
"There can only be a template file or inline render method per variant."
|
@@ -236,8 +240,9 @@ module ViewComponent
|
|
236
240
|
end
|
237
241
|
|
238
242
|
def compiled_template(file_path)
|
239
|
-
handler = ActionView::Template.handler_for_extension(File.extname(file_path).
|
243
|
+
handler = ActionView::Template.handler_for_extension(File.extname(file_path).delete("."))
|
240
244
|
template = File.read(file_path)
|
245
|
+
template.rstrip! if component_class.strip_trailing_whitespace?
|
241
246
|
|
242
247
|
if handler.method(:call).parameters.length > 1
|
243
248
|
handler.call(component_class, template)
|
@@ -259,5 +264,14 @@ module ViewComponent
|
|
259
264
|
"call"
|
260
265
|
end
|
261
266
|
end
|
267
|
+
|
268
|
+
def should_compile_superclass?
|
269
|
+
development? &&
|
270
|
+
templates.empty? &&
|
271
|
+
!(
|
272
|
+
component_class.instance_methods(false).include?(:call) ||
|
273
|
+
component_class.private_instance_methods(false).include?(:call)
|
274
|
+
)
|
275
|
+
end
|
262
276
|
end
|
263
277
|
end
|
@@ -28,7 +28,7 @@ module ViewComponent
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def types
|
31
|
-
" → [#{@method.tag(:return).types.join(
|
31
|
+
" → [#{@method.tag(:return).types.join(",")}]" if @method.tag(:return)&.types && show_types?
|
32
32
|
end
|
33
33
|
|
34
34
|
def signature_or_name
|
@@ -77,7 +77,8 @@ module ViewComponent
|
|
77
77
|
options = app.config.view_component
|
78
78
|
|
79
79
|
if options.show_previews && !options.preview_paths.empty?
|
80
|
-
ActiveSupport::Dependencies.autoload_paths
|
80
|
+
paths_to_add = options.preview_paths - ActiveSupport::Dependencies.autoload_paths
|
81
|
+
ActiveSupport::Dependencies.autoload_paths.concat(paths_to_add) if paths_to_add.any?
|
81
82
|
end
|
82
83
|
end
|
83
84
|
|
@@ -133,10 +134,10 @@ module ViewComponent
|
|
133
134
|
|
134
135
|
initializer "compiler mode" do |app|
|
135
136
|
ViewComponent::Compiler.mode = if Rails.env.development? || Rails.env.test?
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
137
|
+
ViewComponent::Compiler::DEVELOPMENT_MODE
|
138
|
+
else
|
139
|
+
ViewComponent::Compiler::PRODUCTION_MODE
|
140
|
+
end
|
140
141
|
end
|
141
142
|
|
142
143
|
config.after_initialize do |app|
|
@@ -34,13 +34,14 @@ module ViewComponent
|
|
34
34
|
def with_output_buffer(buf = nil)
|
35
35
|
unless buf
|
36
36
|
buf = ActionView::OutputBuffer.new
|
37
|
+
# rubocop:disable Style/SafeNavigation
|
37
38
|
if output_buffer && output_buffer.respond_to?(:encoding)
|
38
39
|
buf.force_encoding(output_buffer.encoding)
|
39
40
|
end
|
41
|
+
# rubocop:enable Style/SafeNavigation
|
40
42
|
end
|
41
43
|
|
42
44
|
output_buffer.push(buf)
|
43
|
-
result = nil
|
44
45
|
|
45
46
|
begin
|
46
47
|
yield
|
@@ -65,13 +66,13 @@ module ViewComponent
|
|
65
66
|
def with_output_buffer(buf = nil)
|
66
67
|
unless buf
|
67
68
|
buf = ActionView::OutputBuffer.new
|
69
|
+
# rubocop:disable Style/SafeNavigation
|
68
70
|
if @output_buffer && @output_buffer.respond_to?(:encoding)
|
69
71
|
buf.force_encoding(@output_buffer.encoding)
|
70
72
|
end
|
73
|
+
# rubocop:enable Style/SafeNavigation
|
71
74
|
end
|
72
75
|
|
73
|
-
result = nil
|
74
|
-
|
75
76
|
if @output_buffer.is_a?(OutputBufferStack)
|
76
77
|
@output_buffer.push(buf)
|
77
78
|
|
@@ -5,6 +5,17 @@ module ViewComponent
|
|
5
5
|
# In older rails versions, using a concern isn't a good idea here because they appear to not work with
|
6
6
|
# Module#prepend and class methods.
|
7
7
|
def self.included(base)
|
8
|
+
if base != ViewComponent::Base
|
9
|
+
# :nocov:
|
10
|
+
location = Kernel.caller_locations(1, 1)[0]
|
11
|
+
|
12
|
+
warn(
|
13
|
+
"warning: ViewComponent::PolymorphicSlots is now included in ViewComponent::Base by default "\
|
14
|
+
"and can be removed from #{location.path}:#{location.lineno}"
|
15
|
+
)
|
16
|
+
# :nocov:
|
17
|
+
end
|
18
|
+
|
8
19
|
base.singleton_class.prepend(ClassMethods)
|
9
20
|
base.include(InstanceMethods)
|
10
21
|
end
|
@@ -45,8 +56,12 @@ module ViewComponent
|
|
45
56
|
"#{slot_name}_#{poly_type}"
|
46
57
|
end
|
47
58
|
|
48
|
-
# Deprecated: Will be removed in 3.0
|
49
59
|
define_method(setter_name) do |*args, &block|
|
60
|
+
ViewComponent::Deprecation.warn(
|
61
|
+
"polymorphic slot setters like `#{setter_name}` are deprecated and will be removed in"\
|
62
|
+
"ViewComponent v3.0.0.\n\nUse `with_#{setter_name}` instead."
|
63
|
+
)
|
64
|
+
|
50
65
|
set_polymorphic_slot(slot_name, poly_type, *args, &block)
|
51
66
|
end
|
52
67
|
ruby2_keywords(setter_name.to_sym) if respond_to?(:ruby2_keywords, true)
|
@@ -57,7 +72,7 @@ module ViewComponent
|
|
57
72
|
ruby2_keywords(:"with_#{setter_name}") if respond_to?(:ruby2_keywords, true)
|
58
73
|
end
|
59
74
|
|
60
|
-
|
75
|
+
registered_slots[slot_name] = {
|
61
76
|
collection: collection,
|
62
77
|
renderable_hash: renderable_hash
|
63
78
|
}
|
@@ -14,7 +14,7 @@ module ViewComponent # :nodoc:
|
|
14
14
|
block: block,
|
15
15
|
component: component,
|
16
16
|
locals: {},
|
17
|
-
template: "view_components/preview"
|
17
|
+
template: "view_components/preview"
|
18
18
|
}
|
19
19
|
end
|
20
20
|
|
@@ -66,10 +66,12 @@ module ViewComponent # :nodoc:
|
|
66
66
|
name.chomp("Preview").underscore
|
67
67
|
end
|
68
68
|
|
69
|
+
# rubocop:disable Style/TrivialAccessors
|
69
70
|
# Setter for layout name.
|
70
71
|
def layout(layout_name)
|
71
72
|
@layout = layout_name
|
72
73
|
end
|
74
|
+
# rubocop:enable Style/TrivialAccessors
|
73
75
|
|
74
76
|
# Returns the relative path (from preview_path) to the preview example template if the template exists
|
75
77
|
def preview_example_template_path(example)
|
@@ -87,15 +89,15 @@ module ViewComponent # :nodoc:
|
|
87
89
|
end
|
88
90
|
|
89
91
|
path = Dir["#{preview_path}/#{preview_name}_preview/#{example}.html.*"].first
|
90
|
-
Pathname.new(path)
|
91
|
-
relative_path_from(Pathname.new(preview_path))
|
92
|
-
to_s
|
93
|
-
sub(/\..*$/, "")
|
92
|
+
Pathname.new(path)
|
93
|
+
.relative_path_from(Pathname.new(preview_path))
|
94
|
+
.to_s
|
95
|
+
.sub(/\..*$/, "")
|
94
96
|
end
|
95
97
|
|
96
98
|
# Returns the method body for the example from the preview file.
|
97
99
|
def preview_source(example)
|
98
|
-
source =
|
100
|
+
source = instance_method(example.to_sym).source.split("\n")
|
99
101
|
source[1...(source.size - 1)].join("\n")
|
100
102
|
end
|
101
103
|
|
@@ -15,11 +15,21 @@ module ViewComponent
|
|
15
15
|
#
|
16
16
|
# MyComponentTest -> MyComponentPreview etc.
|
17
17
|
#
|
18
|
+
# In RSpec, `Preview` is appended to `described_class`.
|
19
|
+
#
|
18
20
|
# @param preview [String] The name of the preview to be rendered.
|
19
21
|
# @return [Nokogiri::HTML]
|
20
22
|
def render_preview(name)
|
21
23
|
begin
|
22
|
-
preview_klass =
|
24
|
+
preview_klass = if respond_to?(:described_class)
|
25
|
+
if described_class.nil?
|
26
|
+
raise "`render_preview` expected a described_class, but it is nil."
|
27
|
+
end
|
28
|
+
|
29
|
+
"#{described_class}Preview"
|
30
|
+
else
|
31
|
+
self.class.name.gsub("Test", "Preview")
|
32
|
+
end
|
23
33
|
preview_klass = preview_klass.constantize
|
24
34
|
rescue NameError
|
25
35
|
raise NameError.new(
|
@@ -4,7 +4,7 @@ module ViewComponent
|
|
4
4
|
module RenderingMonkeyPatch # :nodoc:
|
5
5
|
def render(options = {}, args = {})
|
6
6
|
if options.respond_to?(:render_in)
|
7
|
-
self.response_body = options.render_in(
|
7
|
+
self.response_body = options.render_in(view_context)
|
8
8
|
else
|
9
9
|
super
|
10
10
|
end
|
@@ -30,7 +30,7 @@ module ViewComponent
|
|
30
30
|
|
31
31
|
slot_names.each do |slot_name|
|
32
32
|
# Ensure slot_name isn't already declared
|
33
|
-
if
|
33
|
+
if slots.key?(slot_name)
|
34
34
|
raise ArgumentError.new("#{slot_name} slot declared multiple times")
|
35
35
|
end
|
36
36
|
|
@@ -73,7 +73,7 @@ module ViewComponent
|
|
73
73
|
class_name = "ViewComponent::Slot" unless class_name.present?
|
74
74
|
|
75
75
|
# Register the slot on the component
|
76
|
-
|
76
|
+
slots[slot_name] = {
|
77
77
|
class_name: class_name,
|
78
78
|
instance_variable_name: instance_variable_name,
|
79
79
|
collection: collection
|
@@ -84,7 +84,7 @@ module ViewComponent
|
|
84
84
|
def inherited(child)
|
85
85
|
# Clone slot configuration into child class
|
86
86
|
# see #test_slots_pollution
|
87
|
-
child.slots =
|
87
|
+
child.slots = slots.clone
|
88
88
|
|
89
89
|
super
|
90
90
|
end
|
@@ -106,7 +106,7 @@ module ViewComponent
|
|
106
106
|
#
|
107
107
|
def slot(slot_name, **args, &block)
|
108
108
|
# Raise ArgumentError if `slot` doesn't exist
|
109
|
-
unless slots.
|
109
|
+
unless slots.key?(slot_name)
|
110
110
|
raise ArgumentError.new "Unknown slot '#{slot_name}' - expected one of '#{slots.keys}'"
|
111
111
|
end
|
112
112
|
|
@@ -123,8 +123,7 @@ module ViewComponent
|
|
123
123
|
slot_instance = args.present? ? slot_class.new(**args) : slot_class.new
|
124
124
|
|
125
125
|
# Capture block and assign to slot_instance#content
|
126
|
-
|
127
|
-
slot_instance.content = view_context.capture(&block).to_s.strip.html_safe if block_given?
|
126
|
+
slot_instance.content = view_context.capture(&block).to_s.strip.html_safe if block
|
128
127
|
|
129
128
|
if slot[:collection]
|
130
129
|
# Initialize instance variable as an empty array
|
@@ -9,7 +9,7 @@ module ViewComponent
|
|
9
9
|
|
10
10
|
RESERVED_NAMES = {
|
11
11
|
singular: %i[content render].freeze,
|
12
|
-
plural: %i[contents renders].freeze
|
12
|
+
plural: %i[contents renders].freeze
|
13
13
|
}.freeze
|
14
14
|
|
15
15
|
# Setup component slot state
|
@@ -190,20 +190,20 @@ module ViewComponent
|
|
190
190
|
# Clone slot configuration into child class
|
191
191
|
# see #test_slots_pollution
|
192
192
|
def inherited(child)
|
193
|
-
child.registered_slots =
|
193
|
+
child.registered_slots = registered_slots.clone
|
194
194
|
super
|
195
195
|
end
|
196
196
|
|
197
197
|
private
|
198
198
|
|
199
199
|
def register_slot(slot_name, **kwargs)
|
200
|
-
|
200
|
+
registered_slots[slot_name] = define_slot(slot_name, **kwargs)
|
201
201
|
end
|
202
202
|
|
203
203
|
def define_slot(slot_name, collection:, callable:)
|
204
204
|
# Setup basic slot data
|
205
205
|
slot = {
|
206
|
-
collection: collection
|
206
|
+
collection: collection
|
207
207
|
}
|
208
208
|
return slot unless callable
|
209
209
|
|
@@ -254,7 +254,7 @@ module ViewComponent
|
|
254
254
|
end
|
255
255
|
|
256
256
|
def raise_if_slot_registered(slot_name)
|
257
|
-
if
|
257
|
+
if registered_slots.key?(slot_name)
|
258
258
|
# TODO remove? This breaks overriding slots when slots are inherited
|
259
259
|
raise ArgumentError.new(
|
260
260
|
"#{self} declares the #{slot_name} slot multiple times.\n\n" \
|
@@ -287,8 +287,6 @@ module ViewComponent
|
|
287
287
|
|
288
288
|
if slot[:collection]
|
289
289
|
[]
|
290
|
-
else
|
291
|
-
nil
|
292
290
|
end
|
293
291
|
end
|
294
292
|
|
@@ -305,7 +303,7 @@ module ViewComponent
|
|
305
303
|
# 2. Since we've to pass block content to components when calling
|
306
304
|
# `render`, evaluating the block here would require us to call
|
307
305
|
# `view_context.capture` twice, which is slower
|
308
|
-
slot.__vc_content_block = block if
|
306
|
+
slot.__vc_content_block = block if block
|
309
307
|
|
310
308
|
# If class
|
311
309
|
if slot_definition[:renderable]
|
@@ -321,7 +319,7 @@ module ViewComponent
|
|
321
319
|
# methods like `content_tag` as well as parent component state.
|
322
320
|
renderable_function = slot_definition[:renderable_function].bind(self)
|
323
321
|
renderable_value =
|
324
|
-
if
|
322
|
+
if block
|
325
323
|
renderable_function.call(*args) do |*rargs|
|
326
324
|
view_context.capture(*rargs, &block)
|
327
325
|
end
|
@@ -1,15 +1,37 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "view_component/render_preview_helper"
|
4
|
+
require "view_component/capybara_simple_session"
|
4
5
|
|
5
6
|
module ViewComponent
|
6
7
|
module TestHelpers
|
7
8
|
begin
|
8
9
|
require "capybara/minitest"
|
10
|
+
|
9
11
|
include Capybara::Minitest::Assertions
|
10
12
|
|
13
|
+
CapybaraSimpleSession::DSL_METHODS.each do |method|
|
14
|
+
if RUBY_VERSION >= "2.7"
|
15
|
+
class_eval <<~METHOD, __FILE__, __LINE__ + 1
|
16
|
+
def #{method}(...)
|
17
|
+
page.method("#{method}").call(...)
|
18
|
+
end
|
19
|
+
METHOD
|
20
|
+
else
|
21
|
+
define_method method do |*args, &block|
|
22
|
+
page.send method, *args, &block
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.included(mod)
|
28
|
+
Capybara::Node::Simple.send(:define_method, :to_capybara_node) do
|
29
|
+
self
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
11
33
|
def page
|
12
|
-
|
34
|
+
@page ||= CapybaraSimpleSession.new(rendered_content)
|
13
35
|
end
|
14
36
|
|
15
37
|
def refute_component_rendered
|
@@ -32,6 +54,9 @@ module ViewComponent
|
|
32
54
|
# @private
|
33
55
|
attr_reader :rendered_content
|
34
56
|
|
57
|
+
# Returns the result of a render_inline call.
|
58
|
+
#
|
59
|
+
# @return [String]
|
35
60
|
def rendered_component
|
36
61
|
ViewComponent::Deprecation.warn(
|
37
62
|
"`rendered_component` is deprecated and will be removed in v3.0.0. " \
|
@@ -52,6 +77,7 @@ module ViewComponent
|
|
52
77
|
# @param component [ViewComponent::Base, ViewComponent::Collection] The instance of the component to be rendered.
|
53
78
|
# @return [Nokogiri::HTML]
|
54
79
|
def render_inline(component, **args, &block)
|
80
|
+
@page = nil
|
55
81
|
@rendered_content =
|
56
82
|
if Rails.version.to_f >= 6.1
|
57
83
|
controller.view_context.render(component, args, &block)
|
@@ -73,6 +99,7 @@ module ViewComponent
|
|
73
99
|
# assert_text("Hello, World!")
|
74
100
|
# ```
|
75
101
|
def render_in_view_context(&block)
|
102
|
+
@page = nil
|
76
103
|
@rendered_content = controller.view_context.instance_exec(&block)
|
77
104
|
Nokogiri::HTML.fragment(@rendered_content)
|
78
105
|
end
|
@@ -9,7 +9,7 @@ module ViewComponent
|
|
9
9
|
module Translatable
|
10
10
|
extend ActiveSupport::Concern
|
11
11
|
|
12
|
-
HTML_SAFE_TRANSLATION_KEY = /(?:_|\b)html\z
|
12
|
+
HTML_SAFE_TRANSLATION_KEY = /(?:_|\b)html\z/
|
13
13
|
|
14
14
|
included do
|
15
15
|
class_attribute :i18n_backend, instance_writer: false, instance_predicate: false
|
@@ -23,14 +23,13 @@ module ViewComponent
|
|
23
23
|
def build_i18n_backend
|
24
24
|
return if CompileCache.compiled? self
|
25
25
|
|
26
|
-
if (translation_files = _sidecar_files(%w[yml yaml])).any?
|
27
|
-
|
26
|
+
self.i18n_backend = if (translation_files = _sidecar_files(%w[yml yaml])).any?
|
27
|
+
# Returning nil cleans up if translations file has been removed since the last compilation
|
28
|
+
|
29
|
+
I18nBackend.new(
|
28
30
|
i18n_scope: i18n_scope,
|
29
|
-
load_paths: translation_files
|
31
|
+
load_paths: translation_files
|
30
32
|
)
|
31
|
-
else
|
32
|
-
# Cleanup if translations file has been removed since the last compilation
|
33
|
-
self.i18n_backend = nil
|
34
33
|
end
|
35
34
|
end
|
36
35
|
end
|
@@ -50,7 +49,7 @@ module ViewComponent
|
|
50
49
|
|
51
50
|
def scope_data(data)
|
52
51
|
@i18n_scope.reverse_each do |part|
|
53
|
-
data = {
|
52
|
+
data = {part => data}
|
54
53
|
end
|
55
54
|
data
|
56
55
|
end
|
@@ -95,7 +94,7 @@ module ViewComponent
|
|
95
94
|
super(key, locale: locale, **options)
|
96
95
|
end
|
97
96
|
end
|
98
|
-
|
97
|
+
alias_method :t, :translate
|
99
98
|
|
100
99
|
# Exposes .i18n_scope as an instance method
|
101
100
|
def i18n_scope
|
@@ -109,7 +108,7 @@ module ViewComponent
|
|
109
108
|
# It's assumed here that objects loaded by the i18n backend will respond to `#html_safe?`.
|
110
109
|
# It's reasonable that if we're in Rails, `active_support/core_ext/string/output_safety.rb`
|
111
110
|
# will provide this to `Object`.
|
112
|
-
translation.html_safe
|
111
|
+
translation.html_safe
|
113
112
|
end
|
114
113
|
end
|
115
114
|
|
data/lib/view_component.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: view_component
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.60.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GitHub Open Source
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-07-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -185,19 +185,19 @@ dependencies:
|
|
185
185
|
- !ruby/object:Gem::Version
|
186
186
|
version: '13.0'
|
187
187
|
- !ruby/object:Gem::Dependency
|
188
|
-
name:
|
188
|
+
name: standard
|
189
189
|
requirement: !ruby/object:Gem::Requirement
|
190
190
|
requirements:
|
191
191
|
- - "~>"
|
192
192
|
- !ruby/object:Gem::Version
|
193
|
-
version:
|
193
|
+
version: '1'
|
194
194
|
type: :development
|
195
195
|
prerelease: false
|
196
196
|
version_requirements: !ruby/object:Gem::Requirement
|
197
197
|
requirements:
|
198
198
|
- - "~>"
|
199
199
|
- !ruby/object:Gem::Version
|
200
|
-
version:
|
200
|
+
version: '1'
|
201
201
|
- !ruby/object:Gem::Dependency
|
202
202
|
name: simplecov
|
203
203
|
requirement: !ruby/object:Gem::Requirement
|
@@ -325,6 +325,7 @@ files:
|
|
325
325
|
- lib/rails/generators/test_unit/templates/component_test.rb.tt
|
326
326
|
- lib/view_component.rb
|
327
327
|
- lib/view_component/base.rb
|
328
|
+
- lib/view_component/capybara_simple_session.rb
|
328
329
|
- lib/view_component/collection.rb
|
329
330
|
- lib/view_component/compile_cache.rb
|
330
331
|
- lib/view_component/compiler.rb
|