view_component 2.12.0 → 2.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of view_component might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8661a26312dc3649e30ee17fbc94f6fd73be410067f8a7fac8b6aaefc4338d11
4
- data.tar.gz: 1024034624c49bb65bfe1fc3466886ffa60cd9699a58c7427ea6dbedb9baa03b
3
+ metadata.gz: 9ccdcdb195fbc49953c3663294a7345eb012f88344d1375456a07c6ef781b1d8
4
+ data.tar.gz: b2cf7cb1139e318f2ca7712242e610c31a42c78448a5d3151db1550a41e00355
5
5
  SHA512:
6
- metadata.gz: 24ddc5373c2fbb7cd81ef4f4077ea66a30ea797fbb9d4c444135d3d8590b5151596fddc18f5bc5e1f18fec65ca87572858513409385489524c11c1929cff3567
7
- data.tar.gz: cf63eb464e88e02310cb55f9c6c779c31622174777dbd1f39a24d0e9ac40407c555ac51012c5ea8c06bdeccac7aab880475c02c678c3fbff9ef22f5ca6cf797e
6
+ metadata.gz: 72657816a14d62166791f37d2f83cfd81efe8f186678bcf21e40c50d38d64663fbc4e2b9fe881820ba26f2a7ea258d5d6c0cb47a73132d5443f66db3489d1921
7
+ data.tar.gz: e7abe1071862dfb3208e2615176fd352b0f6cd6e7ef947b1dfb4528e435682908cd01ab8091b33612b234eebf7121a7dd00e5cb8890bc6ae1160fdf5bcbdcd3f
@@ -1,5 +1,50 @@
1
1
  # master
2
2
 
3
+ # 2.16.0
4
+
5
+ * Add `--sidecar` option to the erb, haml and slim generators. Places the generated template in the sidecar directory.
6
+
7
+ *Michael van Rooijen*
8
+
9
+ # 2.15.0
10
+
11
+ * Add support for templates as ViewComponent::Preview examples.
12
+
13
+ *Juan Manuel Ramallo
14
+
15
+ # 2.14.1
16
+
17
+ * Allow using `render_inline` in test when the render monkey patch is disabled with `config.view_component.render_monkey_patch_enabled = false` in versions of Rails < 6.1.
18
+
19
+ *Clément Joubert*
20
+
21
+ * Fix kwargs warnings in slotable.
22
+
23
+ Fixes:
24
+
25
+ ```
26
+ view_component/lib/view_component/slotable.rb:98: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
27
+ view_component/test/app/components/slots_component.rb:18: warning: The called method `initialize' is defined here
28
+ ```
29
+
30
+ *Eileen M. Uchitelle*
31
+
32
+ # 2.14.0
33
+
34
+ * Add `config.preview_paths` to support multiple locations of component preview files. Deprecate `config.preview_path`.
35
+
36
+ *Tomas Celizna*
37
+
38
+ * Only print warning about a missing capybara dependency if the `DEBUG` environment variable is set.
39
+
40
+ *Richard Macklin*
41
+
42
+ # 2.13.0
43
+
44
+ * Add the ability to disable the render monkey patch with `config.view_component.render_monkey_patch_enabled`. In versions of Rails < 6.1, add `render_component` and `render_component_to_string` methods which can be used for rendering components instead of `render`.
45
+
46
+ *Johannes Engl*
47
+
3
48
  # 2.12.0
4
49
 
5
50
  * Implement Slots as potential successor to Content Areas.
data/README.md CHANGED
@@ -341,15 +341,25 @@ As an alternative, views and other assets can be placed in a sidecar directory w
341
341
  ```
342
342
  app/components
343
343
  ├── ...
344
- ├── test_component.rb
345
- ├── test_component
346
- | ├── test_component.css
347
- | ├── test_component.html.erb
348
- | └── test_component.js
344
+ ├── example_component.rb
345
+ ├── example_component
346
+ | ├── example_component.css
347
+ | ├── example_component.html.erb
348
+ | └── example_component.js
349
349
  ├── ...
350
350
 
351
351
  ```
352
352
 
353
+ To generate a component with a sidecar directory, use the `--sidecar` flag:
354
+
355
+ ```
356
+ bin/rails generate component Example title content --sidecar
357
+ invoke test_unit
358
+ create test/components/example_component_test.rb
359
+ create app/components/example_component.rb
360
+ create app/components/example_component/example_component.html.erb
361
+ ```
362
+
353
363
  ### Conditional Rendering
354
364
 
355
365
  Components can implement a `#render?` method to be called after initialization to determine if the component should render.
@@ -664,11 +674,11 @@ class TestComponentPreview < ViewComponent::Preview
664
674
  end
665
675
  ```
666
676
 
667
- Preview classes live in `test/components/previews`, which can be configured using the `preview_path` option:
677
+ Preview classes live in `test/components/previews`, which can be configured using the `preview_paths` option:
668
678
 
669
679
  `config/application.rb`
670
680
  ```ruby
671
- config.view_component.preview_path = "#{Rails.root}/lib/component_previews"
681
+ config.view_component.preview_paths << "#{Rails.root}/lib/component_previews"
672
682
  ```
673
683
 
674
684
  Previews are served from <http://localhost:3000/rails/view_components> by default. To use a different endpoint, set the `preview_route` option:
@@ -680,6 +690,57 @@ config.view_component.preview_route = "/previews"
680
690
 
681
691
  This example will make the previews available from <http://localhost:3000/previews>.
682
692
 
693
+ #### Preview templates
694
+
695
+ Given a preview `test/components/previews/cell_component_preview.rb`, template files can be defined at `test/components/previews/cell_component_preview/`:
696
+
697
+ `test/components/previews/cell_component_preview.rb`
698
+ ```ruby
699
+ class CellComponentPreview < ViewComponent::Preview
700
+ def default
701
+ end
702
+ end
703
+ ```
704
+
705
+ `test/components/previews/cell_component_preview/default.html.erb`
706
+ ```erb
707
+ <table class="table">
708
+ <tbody>
709
+ <tr>
710
+ <%= render CellComponent.new %>
711
+ </tr>
712
+ </tbody>
713
+ </div>
714
+ ```
715
+
716
+ To use a different location for preview templates, pass the `template` argument:
717
+ (the path should be relative to `config.view_component.preview_path`):
718
+
719
+ `test/components/previews/cell_component_preview.rb`
720
+ ```ruby
721
+ class CellComponentPreview < ViewComponent::Preview
722
+ def default
723
+ render_with_template(template: 'custom_cell_component_preview/my_preview_template')
724
+ end
725
+ end
726
+ ```
727
+
728
+ Values from `params` can be accessed through `locals`:
729
+
730
+ `test/components/previews/cell_component_preview.rb`
731
+ ```ruby
732
+ class CellComponentPreview < ViewComponent::Preview
733
+ def default(title: "Default title", subtitle: "A subtitle")
734
+ render_with_template(locals: {
735
+ title: title,
736
+ subtitle: subtitle
737
+ })
738
+ end
739
+ end
740
+ ```
741
+
742
+ Which enables passing in a value with <http://localhost:3000/rails/components/cell_component/default?title=Custom+title&subtitle=Another+subtitle>.
743
+
683
744
  #### Configuring TestController
684
745
 
685
746
  Component tests and previews assume the existence of an `ApplicationController` class, which be can be configured using the `test_controller` option:
@@ -708,7 +769,19 @@ To use component previews:
708
769
 
709
770
  `config/application.rb`
710
771
  ```ruby
711
- config.view_component.preview_path = "#{Rails.root}/spec/components/previews"
772
+ config.view_component.preview_paths << "#{Rails.root}/spec/components/previews"
773
+ ```
774
+
775
+ ### Disabling the render monkey patch (Rails < 6.1)
776
+
777
+ In order to [avoid conflicts](https://github.com/github/view_component/issues/288) between ViewComponent and other gems that also monkey patch the `render` method, it is possible to configure ViewComponent to not include the render monkey patch:
778
+
779
+ `config.view_component.render_monkey_patch_enabled = false # defaults to true`
780
+
781
+ With the monkey patch disabled, use `render_component` (or `render_component_to_string`) instead:
782
+
783
+ ```
784
+ <%= render_component Component.new(message: "bar") %>
712
785
  ```
713
786
 
714
787
  ### Sidecar assets (experimental)
@@ -917,6 +990,11 @@ ViewComponent is built by:
917
990
  |@maxbeizer|@franco|@tbroad-ramsey|@jensljungblad|@bbugh|
918
991
  |Nashville, TN|Switzerland|Spring Hill, TN|New York, NY|Austin, TX|
919
992
 
993
+ |<img src="https://avatars.githubusercontent.com/johannesengl?s=256" alt="johannesengl" width="128" />|<img src="https://avatars.githubusercontent.com/czj?s=256" alt="czj" width="128" />|<img src="https://avatars.githubusercontent.com/mrrooijen?s=256" alt="mrrooijen" width="128" />|
994
+ |:---:|:---:|:---:|
995
+ |@johannesengl|@czj|@mrrooijen|
996
+ |Berlin, Germany|Paris, France|The Netherlands|
997
+
920
998
  ## License
921
999
 
922
1000
  ViewComponent is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -24,11 +24,16 @@ class ViewComponentsController < Rails::ApplicationController # :nodoc:
24
24
  render "view_components/previews"
25
25
  else
26
26
  prepend_application_view_paths
27
+ prepend_preview_examples_view_path
27
28
  @example_name = File.basename(params[:path])
28
29
  @render_args = @preview.render_args(@example_name, params: params.permit!)
29
30
  layout = @render_args[:layout]
30
- opts = layout.nil? ? {} : { layout: layout }
31
- render "view_components/preview", opts
31
+ template = @render_args[:template]
32
+ locals = @render_args[:locals]
33
+ opts = {}
34
+ opts[:layout] = layout if layout.present?
35
+ opts[:locals] = locals if locals.present?
36
+ render template, opts # rubocop:disable GitHub/RailsControllerRenderLiteral
32
37
  end
33
38
  end
34
39
 
@@ -59,4 +64,8 @@ class ViewComponentsController < Rails::ApplicationController # :nodoc:
59
64
  def prepend_application_view_paths
60
65
  prepend_view_path Rails.root.join("app/views") if defined?(Rails.root)
61
66
  end
67
+
68
+ def prepend_preview_examples_view_path
69
+ prepend_view_path(ViewComponent::Base.preview_paths)
70
+ end
62
71
  end
@@ -6,13 +6,22 @@ module Erb
6
6
  module Generators
7
7
  class ComponentGenerator < Base
8
8
  source_root File.expand_path("templates", __dir__)
9
+ class_option :sidecar, type: :boolean, default: false
9
10
 
10
11
  def copy_view_file
11
- template "component.html.erb", File.join("app/components", class_path, "#{file_name}_component.html.erb")
12
+ template "component.html.erb", destination
12
13
  end
13
14
 
14
15
  private
15
16
 
17
+ def destination
18
+ if options["sidecar"]
19
+ File.join("app/components", class_path, "#{file_name}_component", "#{file_name}_component.html.erb")
20
+ else
21
+ File.join("app/components", class_path, "#{file_name}_component.html.erb")
22
+ end
23
+ end
24
+
16
25
  def file_name
17
26
  @_file_name ||= super.sub(/_component\z/i, "")
18
27
  end
@@ -6,13 +6,22 @@ module Haml
6
6
  module Generators
7
7
  class ComponentGenerator < Erb::Generators::ComponentGenerator
8
8
  source_root File.expand_path("templates", __dir__)
9
+ class_option :sidecar, type: :boolean, default: false
9
10
 
10
11
  def copy_view_file
11
- template "component.html.haml", File.join("app/components", class_path, "#{file_name}_component.html.haml")
12
+ template "component.html.haml", destination
12
13
  end
13
14
 
14
15
  private
15
16
 
17
+ def destination
18
+ if options["sidecar"]
19
+ File.join("app/components", class_path, "#{file_name}_component", "#{file_name}_component.html.haml")
20
+ else
21
+ File.join("app/components", class_path, "#{file_name}_component.html.haml")
22
+ end
23
+ end
24
+
16
25
  def file_name
17
26
  @_file_name ||= super.sub(/_component\z/i, "")
18
27
  end
@@ -6,13 +6,22 @@ module Slim
6
6
  module Generators
7
7
  class ComponentGenerator < Erb::Generators::ComponentGenerator
8
8
  source_root File.expand_path("templates", __dir__)
9
+ class_option :sidecar, type: :boolean, default: false
9
10
 
10
11
  def copy_view_file
11
- template "component.html.slim", File.join("app/components", class_path, "#{file_name}_component.html.slim")
12
+ template "component.html.slim", destination
12
13
  end
13
14
 
14
15
  private
15
16
 
17
+ def destination
18
+ if options["sidecar"]
19
+ File.join("app/components", class_path, "#{file_name}_component", "#{file_name}_component.html.slim")
20
+ else
21
+ File.join("app/components", class_path, "#{file_name}_component.html.slim")
22
+ end
23
+ end
24
+
16
25
  def file_name
17
26
  @_file_name ||= super.sub(/_component\z/i, "")
18
27
  end
@@ -7,6 +7,7 @@ module ViewComponent
7
7
 
8
8
  autoload :Base
9
9
  autoload :Preview
10
+ autoload :PreviewTemplateError
10
11
  autoload :TestHelpers
11
12
  autoload :TestCase
12
13
  autoload :TemplateError
@@ -162,6 +162,9 @@ module ViewComponent
162
162
  mattr_accessor :test_controller
163
163
  @@test_controller = "ApplicationController"
164
164
 
165
+ # Configure if render monkey patches should be included or not in Rails <6.1.
166
+ mattr_accessor :render_monkey_patch_enabled, instance_writer: false, default: true
167
+
165
168
  class << self
166
169
  attr_accessor :source_location
167
170
 
@@ -6,15 +6,24 @@ require "view_component"
6
6
  module ViewComponent
7
7
  class Engine < Rails::Engine # :nodoc:
8
8
  config.view_component = ActiveSupport::OrderedOptions.new
9
+ config.view_component.preview_paths ||= []
9
10
 
10
11
  initializer "view_component.set_configs" do |app|
11
12
  options = app.config.view_component
12
13
 
14
+ options.render_monkey_patch_enabled = true if options.render_monkey_patch_enabled.nil?
13
15
  options.show_previews = Rails.env.development? if options.show_previews.nil?
14
16
  options.preview_route ||= ViewComponent::Base.preview_route
15
17
 
16
18
  if options.show_previews
17
- options.preview_path ||= defined?(Rails.root) ? "#{Rails.root}/test/components/previews" : nil
19
+ options.preview_paths << "#{Rails.root}/test/components/previews" if defined?(Rails.root)
20
+
21
+ if options.preview_path.present?
22
+ ActiveSupport::Deprecation.warn(
23
+ "`preview_path` will be removed in v3.0.0. Use `preview_paths` instead."
24
+ )
25
+ options.preview_paths << options.preview_path
26
+ end
18
27
  end
19
28
 
20
29
  ActiveSupport.on_load(:view_component) do
@@ -42,21 +51,35 @@ module ViewComponent
42
51
  end
43
52
  end
44
53
 
45
- initializer "view_component.monkey_patch_render" do
54
+ initializer "view_component.monkey_patch_render" do |app|
55
+ next if Rails.version.to_f >= 6.1 || !app.config.view_component.render_monkey_patch_enabled
56
+
46
57
  ActiveSupport.on_load(:action_view) do
47
- if Rails.version.to_f < 6.1
48
- require "view_component/render_monkey_patch"
49
- ActionView::Base.prepend ViewComponent::RenderMonkeyPatch
50
- end
58
+ require "view_component/render_monkey_patch"
59
+ ActionView::Base.prepend ViewComponent::RenderMonkeyPatch
51
60
  end
52
61
 
53
62
  ActiveSupport.on_load(:action_controller) do
54
- if Rails.version.to_f < 6.1
55
- require "view_component/rendering_monkey_patch"
56
- require "view_component/render_to_string_monkey_patch"
57
- ActionController::Base.prepend ViewComponent::RenderingMonkeyPatch
58
- ActionController::Base.prepend ViewComponent::RenderToStringMonkeyPatch
59
- end
63
+ require "view_component/rendering_monkey_patch"
64
+ require "view_component/render_to_string_monkey_patch"
65
+ ActionController::Base.prepend ViewComponent::RenderingMonkeyPatch
66
+ ActionController::Base.prepend ViewComponent::RenderToStringMonkeyPatch
67
+ end
68
+ end
69
+
70
+ initializer "view_component.include_render_component" do |app|
71
+ next if Rails.version.to_f >= 6.1
72
+
73
+ ActiveSupport.on_load(:action_view) do
74
+ require "view_component/render_component_helper"
75
+ ActionView::Base.include ViewComponent::RenderComponentHelper
76
+ end
77
+
78
+ ActiveSupport.on_load(:action_controller) do
79
+ require "view_component/rendering_component_helper"
80
+ require "view_component/render_component_to_string_helper"
81
+ ActionController::Base.include ViewComponent::RenderingComponentHelper
82
+ ActionController::Base.include ViewComponent::RenderComponentToStringHelper
60
83
  end
61
84
  end
62
85
 
@@ -8,7 +8,20 @@ module ViewComponent # :nodoc:
8
8
  extend ActiveSupport::DescendantsTracker
9
9
 
10
10
  def render(component, **args, &block)
11
- { component: component, args: args, block: block }
11
+ {
12
+ args: args,
13
+ block: block,
14
+ component: component,
15
+ locals: {},
16
+ template: "view_components/preview",
17
+ }
18
+ end
19
+
20
+ def render_with_template(template: nil, locals: {})
21
+ {
22
+ template: template,
23
+ locals: locals
24
+ }
12
25
  end
13
26
 
14
27
  class << self
@@ -23,6 +36,8 @@ module ViewComponent # :nodoc:
23
36
  example_params_names = instance_method(example).parameters.map(&:last)
24
37
  provided_params = params.slice(*example_params_names).to_h.symbolize_keys
25
38
  result = provided_params.empty? ? new.public_send(example) : new.public_send(example, **provided_params)
39
+ result ||= {}
40
+ result[:template] = preview_example_template_path(example) if result[:template].nil?
26
41
  @layout = nil unless defined?(@layout)
27
42
  result.merge(layout: @layout)
28
43
  end
@@ -62,16 +77,30 @@ module ViewComponent # :nodoc:
62
77
  @layout = layout_name
63
78
  end
64
79
 
80
+ # Returns the relative path (from preview_path) to the preview example template if the template exists
81
+ def preview_example_template_path(example)
82
+ preview_path = Array(preview_paths).detect do |preview_path|
83
+ Dir["#{preview_path}/#{preview_name}_preview/#{example}.html.*"].first
84
+ end
85
+
86
+ if preview_path.nil?
87
+ raise PreviewTemplateError, "preview template for example #{example} does not exist"
88
+ end
89
+
90
+ path = Dir["#{preview_path}/#{preview_name}_preview/#{example}.html.*"].first
91
+ Pathname.new(path).relative_path_from(Pathname.new(preview_path)).to_s
92
+ end
93
+
65
94
  private
66
95
 
67
96
  def load_previews
68
- if preview_path
97
+ Array(preview_paths).each do |preview_path|
69
98
  Dir["#{preview_path}/**/*_preview.rb"].sort.each { |file| require_dependency file }
70
99
  end
71
100
  end
72
101
 
73
- def preview_path
74
- Base.preview_path
102
+ def preview_paths
103
+ Base.preview_paths
75
104
  end
76
105
 
77
106
  def show_previews
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent
4
+ class PreviewTemplateError < StandardError
5
+ end
6
+ end
@@ -9,8 +9,11 @@ module ViewComponent # :nodoc:
9
9
  included do
10
10
  # Set the location of component previews through app configuration:
11
11
  #
12
- # config.view_component.preview_path = "#{Rails.root}/lib/component_previews"
12
+ # config.view_component.preview_paths << "#{Rails.root}/lib/component_previews"
13
13
  #
14
+ mattr_accessor :preview_paths, instance_writer: false
15
+
16
+ # TODO: deprecated, remove in v3.0.0
14
17
  mattr_accessor :preview_path, instance_writer: false
15
18
 
16
19
  # Enable or disable component previews through app configuration:
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent
4
+ module RenderComponentHelper # :nodoc:
5
+ def render_component(component, &block)
6
+ component.render_in(self, &block)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent
4
+ module RenderComponentToStringHelper # :nodoc:
5
+ def render_component_to_string(component)
6
+ component.render_in(self.view_context)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent
4
+ module RenderingComponentHelper # :nodoc:
5
+ def render_component(component)
6
+ self.response_body = component.render_in(self.view_context)
7
+ end
8
+ end
9
+ end
@@ -95,7 +95,7 @@ module ViewComponent
95
95
  end
96
96
 
97
97
  # Instantiate Slot class, accommodating Slots that don't accept arguments
98
- slot_instance = args.present? ? slot_class.new(args) : slot_class.new
98
+ slot_instance = args.present? ? slot_class.new(**args) : slot_class.new
99
99
 
100
100
  # Capture block and assign to slot_instance#content
101
101
  slot_instance.content = view_context.capture(&block) if block_given?
@@ -14,13 +14,18 @@ module ViewComponent
14
14
  assert_no_selector("body")
15
15
  end
16
16
  rescue LoadError
17
- warn "WARNING in `ViewComponent::TestHelpers`: You must add `capybara` to your Gemfile to use Capybara assertions."
17
+ warn "WARNING in `ViewComponent::TestHelpers`: You must add `capybara` to your Gemfile to use Capybara assertions." if ENV["DEBUG"]
18
18
  end
19
19
 
20
20
  attr_reader :rendered_component
21
21
 
22
22
  def render_inline(component, **args, &block)
23
- @rendered_component = controller.view_context.render(component, args, &block)
23
+ @rendered_component =
24
+ if Rails.version.to_f >= 6.1
25
+ controller.view_context.render(component, args, &block)
26
+ else
27
+ controller.view_context.render_component(component, &block)
28
+ end
24
29
 
25
30
  Nokogiri::HTML.fragment(@rendered_component)
26
31
  end
@@ -3,7 +3,7 @@
3
3
  module ViewComponent
4
4
  module VERSION
5
5
  MAJOR = 2
6
- MINOR = 12
6
+ MINOR = 16
7
7
  PATCH = 0
8
8
 
9
9
  STRING = [MAJOR, MINOR, PATCH].join(".")
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.12.0
4
+ version: 2.16.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: 2020-06-30 00:00:00.000000000 Z
11
+ date: 2020-07-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -203,9 +203,13 @@ files:
203
203
  - lib/view_component/compile_cache.rb
204
204
  - lib/view_component/engine.rb
205
205
  - lib/view_component/preview.rb
206
+ - lib/view_component/preview_template_error.rb
206
207
  - lib/view_component/previewable.rb
208
+ - lib/view_component/render_component_helper.rb
209
+ - lib/view_component/render_component_to_string_helper.rb
207
210
  - lib/view_component/render_monkey_patch.rb
208
211
  - lib/view_component/render_to_string_monkey_patch.rb
212
+ - lib/view_component/rendering_component_helper.rb
209
213
  - lib/view_component/rendering_monkey_patch.rb
210
214
  - lib/view_component/slot.rb
211
215
  - lib/view_component/slotable.rb