view_component 2.5.1 → 2.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 38eb3604bfed89ac05aad6501271fe16c0045db486737fe1b6306ac002339b90
4
- data.tar.gz: 5bd3f94e4f333667a9a534ff3171650a285df654bb45ae7e66a8f8441cfa5d9c
3
+ metadata.gz: 8bc0f4ab66dfca6a89f9882d0df39f5f4a84f479f26dfd0b3334b43fe4f9b4ed
4
+ data.tar.gz: 39128211a1338af6c78a54085b59f48a98411dc282e6248f03fc56229a6d7cd0
5
5
  SHA512:
6
- metadata.gz: df1664a8bb11ccbb88dabb3605039ba15d57308219068341f00d5a85ec189d3dda57b0d6185c9f86526b99646a79e5b1e216e35d49b75439b9a3ea5f4df1ac59
7
- data.tar.gz: 77ca17df33fadfafd7a38acfae6add65a2b2096f6dcc307717f899b2ff8f4e3d7532d68e7ee87b0d6addd8f4aeb47ef5b408e27a15d081c91ef39a5402890098
6
+ metadata.gz: 1aa9fb3d7361efa6a98465c1513e3296abc4495ad30393ed9fbf3febc919a8669184fe2e28fe5afa9666a42b71958787b2bf091b71d4e9889b020d0495769abf
7
+ data.tar.gz: 2ce5ece866ddc50d8aa01cd139c230c10b26aee9d8bbc49f7421b8d191e5b84dbd57d1247b9973f936932dcde9bd925f96c2d3114ac201d98511157b7643bac0
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # master
2
2
 
3
+ # 2.6.0
4
+
5
+ * Add `config.view_component.preview_route` to set the endpoint for component previews. By default `/rails/view_components` is used.
6
+
7
+ *Juan Manuel Ramallo*
8
+
9
+ * Raise error when initializer omits with_collection_parameter.
10
+
11
+ *Joel Hawksley*
12
+
3
13
  # 2.5.1
4
14
 
5
15
  * Compile component before rendering collection.
data/README.md CHANGED
@@ -1,30 +1,13 @@
1
1
  # ViewComponent
2
- A view component framework for Rails.
3
-
4
- **Current Status**: Used in production at GitHub. Because of this, all changes will be thoroughly vetted, which could slow down the process of contributing. We will do our best to actively communicate status of pull requests with any contributors. If you have any substantial changes that you would like to make, it would be great to first [open an issue](http://github.com/github/view_component/issues/new) to discuss them with us.
5
-
6
- ## Migration from ActionView::Component
7
-
8
- This gem used to be called `ActionView::Component`.
9
- See [issue #206] for some background on the name change.
10
- Learn more about what changed and how to migrate [here][migration-info].
11
-
12
- [issue #206]: https://github.com/github/view_component/issues/206
13
- [migration-info]: https://github.com/github/view_component/blob/v2.0.0/README.md#migration-in-progress
14
-
15
- ## Roadmap
16
-
17
- Support for third-party component frameworks was merged into Rails `6.1.0.alpha` in https://github.com/rails/rails/pull/36388 and https://github.com/rails/rails/pull/37919. Our goal with this project is to provide a first-class component framework for this new capability in Rails.
18
-
19
- This gem includes a backport of those changes for Rails `5.0.0` through `6.1.0.alpha`.
2
+ ViewComponent is a framework for building view components that are reusable, testable & encapsulated, in Ruby on Rails.
20
3
 
21
4
  ## Design philosophy
22
-
23
- This library is designed to integrate as seamlessly as possible with Rails, with the [least surprise](https://www.artima.com/intv/ruby4.html).
5
+ ViewComponent is designed to integrate as seamlessly as possible [with Rails](https://rubyonrails.org/doctrine/), with the [least surprise](https://www.artima.com/intv/ruby4.html).
24
6
 
25
7
  ## Compatibility
8
+ ViewComponent is [supported natively](https://edgeguides.rubyonrails.org/layouts_and_rendering.html#rendering-objects) in Rails 6.1, and compatible with Rails 5.0+ via an included [monkey patch](https://github.com/github/view_component/blob/master/lib/view_component/render_monkey_patch.rb).
26
9
 
27
- `view_component` is tested for compatibility with combinations of Ruby `2.4`/`2.5`/`2.6`/`2.7` and Rails `5.0.0`/`5.2.3`/`6.0.0`/`master`.
10
+ ViewComponent is tested for compatibility [with combinations of](https://github.com/github/view_component/blob/22e3d4ccce70d8f32c7375e5a5ccc3f70b22a703/.github/workflows/ruby_on_rails.yml#L10-L11) Ruby 2.4+ and Rails 5+.
28
11
 
29
12
  ## Installation
30
13
 
@@ -44,7 +27,7 @@ require "view_component/engine"
44
27
 
45
28
  ### What are components?
46
29
 
47
- `ViewComponent`s are Ruby classes that are used to render views. They take data as input and return output-safe HTML. Think of them as an evolution of the presenter/decorator/view model pattern, inspired by [React Components](https://reactjs.org/docs/react-component.html).
30
+ ViewComponents are Ruby objects that output HTML. Think of them as an evolution of the presenter pattern, inspired by [React](https://reactjs.org/docs/react-component.html).
48
31
 
49
32
  Components are most effective in cases where view code is reused or benefits from being tested directly.
50
33
 
@@ -52,47 +35,39 @@ Components are most effective in cases where view code is reused or benefits fro
52
35
 
53
36
  #### Testing
54
37
 
55
- Rails encourages testing views with integration tests. This discourages us from testing views thoroughly, due to the overhead of exercising the routing and controller layers in addition to the view.
38
+ Unlike traditional Rails views, ViewComponents can be unit-tested. In the GitHub codebase, component unit tests take around 25 milliseconds each, compared to about six seconds for controller tests.
56
39
 
57
- For partials, this means being tested for each view they are included in, reducing the benefit of reusing them.
40
+ Rails views are typically tested with slow integration tests that also exercise the routing and controller layers in addition to the view. This cost often discourages thorough test coverage.
58
41
 
59
- `ViewComponent`s can be unit-tested. In the GitHub codebase, our component unit tests run in around 25 milliseconds, compared to about six seconds for integration tests.
42
+ With ViewComponent, integration tests can be reserved for end-to-end assertions, with permutations and corner cases covered at the unit level.
60
43
 
61
44
  #### Data Flow
62
45
 
63
- Unlike a method declaration on an object, views do not declare the values they are expected to receive, making it hard to figure out what context is necessary to render them. This often leads to subtle bugs when reusing a view in different contexts.
46
+ Traditional Rails views have an implicit interface, making it hard to reason about what information is needed to render, leading to subtle bugs when rendering the same view in different contexts.
64
47
 
65
- By clearly defining the context necessary to render a `ViewComponent`, they're easier to reuse than partials.
48
+ ViewComponents use a standard Ruby initializer that clearly defines what is needed to render, making them easier (and safer) to reuse than partials.
66
49
 
67
50
  #### Standards
68
51
 
69
52
  Views often fail basic Ruby code quality standards: long methods, deep conditional nesting, and mystery guests abound.
70
53
 
71
- `ViewComponent`s are Ruby objects, making it easy to follow code quality standards.
72
-
73
- #### Code Coverage
74
-
75
- Many common Ruby code coverage tools cannot properly handle coverage of views, making it difficult to audit how thorough tests are and leading to missing coverage in test suites.
76
-
77
- `ViewComponent` is at least partially compatible with code coverage tools, such as SimpleCov.
54
+ ViewComponents are Ruby objects, making it easy to follow (and enforce) code quality standards.
78
55
 
79
56
  ### Building components
80
57
 
81
58
  #### Conventions
82
59
 
83
- Components are subclasses of `ViewComponent::Base` and live in `app/components`. It's recommended to create an `ApplicationComponent` that is a subclass of `ViewComponent::Base` and inherit from that instead.
84
-
85
- Component class names end in -`Component`.
60
+ Components are subclasses of `ViewComponent::Base` and live in `app/components`. It's common practice to create and inherit from an `ApplicationComponent` that is a subclass of `ViewComponent::Base`.
86
61
 
87
- Component module names are plural, as they are for controllers. (`Users::AvatarComponent`)
62
+ Component names end in -`Component`.
88
63
 
89
- Content passed to a `ViewComponent` as a block is captured and assigned to the `content` accessor.
64
+ Component module names are plural, as for controllers and jobs: `Users::AvatarComponent`
90
65
 
91
66
  #### Quick start
92
67
 
93
- Use the component generator to create a new `ViewComponent`.
68
+ Use the component generator to create a new ViewComponent.
94
69
 
95
- The generator accepts the component name and the list of accepted properties as arguments:
70
+ The generator accepts a component name and a list of arguments:
96
71
 
97
72
  ```bash
98
73
  bin/rails generate component Example title content
@@ -102,7 +77,7 @@ bin/rails generate component Example title content
102
77
  create app/components/example_component.html.erb
103
78
  ```
104
79
 
105
- `ViewComponent` includes template generators for the `erb`, `haml`, and `slim` template engines and will use the template engine specified in the Rails configuration (`config.generators.template_engine`) by default.
80
+ ViewComponent includes template generators for the `erb`, `haml`, and `slim` template engines and will default to the template engine specified in `config.generators.template_engine`.
106
81
 
107
82
  The template engine can also be passed as an option to the generator:
108
83
 
@@ -112,7 +87,7 @@ bin/rails generate component Example title content --template-engine slim
112
87
 
113
88
  #### Implementation
114
89
 
115
- A `ViewComponent` is a Ruby file and corresponding template file with the same base name:
90
+ A ViewComponent is a Ruby file and corresponding template file with the same base name:
116
91
 
117
92
  `app/components/test_component.rb`:
118
93
  ```ruby
@@ -128,7 +103,7 @@ end
128
103
  <span title="<%= @title %>"><%= content %></span>
129
104
  ```
130
105
 
131
- Which is rendered in a view as:
106
+ Rendered in a view as:
132
107
 
133
108
  ```erb
134
109
  <%= render(TestComponent.new(title: "my title")) do %>
@@ -136,7 +111,7 @@ Which is rendered in a view as:
136
111
  <% end %>
137
112
  ```
138
113
 
139
- Which returns:
114
+ Returning:
140
115
 
141
116
  ```html
142
117
  <span title="my title">Hello, World!</span>
@@ -144,7 +119,9 @@ Which returns:
144
119
 
145
120
  #### Content Areas
146
121
 
147
- A component can declare additional content areas to be rendered in the component. For example:
122
+ Content passed to a ViewComponent as a block is captured and assigned to the `content` accessor.
123
+
124
+ ViewComponents can declare additional content areas. For example:
148
125
 
149
126
  `app/components/modal_component.rb`:
150
127
  ```ruby
@@ -161,7 +138,7 @@ end
161
138
  </div>
162
139
  ```
163
140
 
164
- Which is rendered in a view as:
141
+ Rendered in a view as:
165
142
 
166
143
  ```erb
167
144
  <%= render(ModalComponent.new) do |component| %>
@@ -174,7 +151,7 @@ Which is rendered in a view as:
174
151
  <% end %>
175
152
  ```
176
153
 
177
- Which returns:
154
+ Returning:
178
155
 
179
156
  ```html
180
157
  <div class="modal">
@@ -185,10 +162,9 @@ Which returns:
185
162
 
186
163
  ### Inline Component
187
164
 
188
- A component can be rendered without any template file as well.
165
+ ViewComponents can render without a template file, by defining a `call` method:
189
166
 
190
167
  `app/components/inline_component.rb`:
191
-
192
168
  ```ruby
193
169
  class InlineComponent < ViewComponent::Base
194
170
  def call
@@ -201,34 +177,30 @@ class InlineComponent < ViewComponent::Base
201
177
  end
202
178
  ```
203
179
 
204
- It is also possible to render variants inline by creating additional `call_` methods.
180
+ It is also possible to define methods for variants:
205
181
 
206
182
  ```ruby
207
183
  class InlineVariantComponent < ViewComponent::Base
208
- def call
209
- link_to "Default", default_path
210
- end
211
-
212
184
  def call_phone
213
185
  link_to "Phone", phone_path
214
186
  end
187
+
188
+ def call
189
+ link_to "Default", default_path
190
+ end
215
191
  end
216
192
  ```
217
193
 
218
- Using a mixture of templates and inline render methods in a component is supported, however only one should be provided per component (or variant).
219
-
220
194
  ### Conditional Rendering
221
195
 
222
- Components can implement a `#render?` method to determine if they should be rendered.
196
+ Components can implement a `#render?` method to be called after initialization to determine if the component should render.
223
197
 
224
- For example, given a component that displays a banner to users who haven't confirmed their email address, the logic for whether to render the banner would need to go in either the component template:
198
+ Traditionally, the logic for whether to render a view could go in either the component template:
225
199
 
226
200
  `app/components/confirm_email_component.html.erb`
227
201
  ```
228
202
  <% if user.requires_confirmation? %>
229
- <div class="alert">
230
- Please confirm your email address.
231
- </div>
203
+ <div class="alert">Please confirm your email address.</div>
232
204
  <% end %>
233
205
  ```
234
206
 
@@ -241,7 +213,7 @@ or the view that renders the component:
241
213
  <% end %>
242
214
  ```
243
215
 
244
- Instead, the `#render?` hook expresses this logic in the Ruby class, simplifying the view:
216
+ Using the `#render?` hook simplifies the view:
245
217
 
246
218
  `app/components/confirm_email_component.rb`
247
219
  ```ruby
@@ -268,19 +240,17 @@ end
268
240
  <%= render(ConfirmEmailComponent.new(user: current_user)) %>
269
241
  ```
270
242
 
271
- To assert that a component has not been rendered, use `refute_component_rendered` from `ViewComponent::TestHelpers`.
243
+ _To assert that a component has not been rendered, use `refute_component_rendered` from `ViewComponent::TestHelpers`._
272
244
 
273
245
  ### Rendering collections
274
246
 
275
- It's possible to render collections with components:
247
+ Use `with_collection` to render a ViewComponent with a collection:
276
248
 
277
249
  `app/view/products/index.html.erb`
278
250
  ``` erb
279
251
  <%= render(ProductComponent.with_collection(@products)) %>
280
252
  ```
281
253
 
282
- Where the `ProductComponent` and associated template might look something like the following. Notice that the constructor must take a `product` and the name of that parameter matches the name of the component.
283
-
284
254
  `app/components/product_component.rb`
285
255
  ``` ruby
286
256
  class ProductComponent < ViewComponent::Base
@@ -290,12 +260,26 @@ class ProductComponent < ViewComponent::Base
290
260
  end
291
261
  ```
292
262
 
293
- `app/components/product_component.html.erb`
294
- ``` erb
295
- <li><%= @product.name %></li>
263
+ [By default](https://github.com/github/view_component/blob/89f8fab4609c1ef2467cf434d283864b3c754473/lib/view_component/base.rb#L249), the component name is used to define the parameter passed into the component from the collection.
264
+
265
+ #### `with_collection_parameter`
266
+
267
+ Use `with_collection_parameter` to change the name of the collection parameter:
268
+
269
+ `app/components/product_component.rb`
270
+ ``` ruby
271
+ class ProductComponent < ViewComponent::Base
272
+ with_collection_parameter :item
273
+
274
+ def initialize(item:)
275
+ @item = item
276
+ end
277
+ end
296
278
  ```
297
279
 
298
- Additionally, extra arguments can be passed to the component and the name of the parameter can be changed:
280
+ #### Additional arguments
281
+
282
+ Additional arguments besides the collection are passed to each component instance:
299
283
 
300
284
  `app/view/products/index.html.erb`
301
285
  ``` erb
@@ -322,7 +306,9 @@ end
322
306
  </li>
323
307
  ```
324
308
 
325
- `ViewComponent` defines a counter variable matching the parameter name above, followed by `_counter`. To access this variable, add it to `initialize` as an argument:
309
+ #### Collection counter
310
+
311
+ ViewComponent defines a counter variable matching the parameter name above, followed by `_counter`. To access the variable, add it to `initialize` as an argument:
326
312
 
327
313
  `app/components/product_component.rb`
328
314
  ``` ruby
@@ -341,140 +327,53 @@ end
341
327
  </li>
342
328
  ```
343
329
 
344
- ### Sidecar assets (experimental)
345
-
346
- We're experimenting with including Javascript and CSS alongside components, sometimes called "sidecar" assets or files.
347
-
348
- To use the Webpacker gem to compile sidecar assets located in `app/components`:
349
-
350
- 1. 1. In `config/webpacker.yml`, add `"app/components"` to the `resolved_paths` array (e.g. `resolved_paths: ["app/components"]`).
351
- 2. In the Webpack entry file (often `app/javascript/packs/application.js`), add an import statement to a helper file, and in the helper file, import the components' Javascript:
352
-
353
- Near the top the entry file, add:
354
-
355
- ```js
356
- import "../components"
357
- ```
358
-
359
- Then add the following to a new file `app/javascript/components.js`:
360
-
361
- ```js
362
- function importAll(r) {
363
- r.keys().forEach(r)
364
- }
365
-
366
- importAll(require.context("../components", true, /_component.js$/))
367
- ```
368
-
369
- Any file with the `_component.js` suffix, for example `app/components/widget_component.js`, will get compiled into the Webpack bundle. If that file itself imports another file, for example `app/components/widget_component.css`, that will also get compiled and bundled into Webpack's output stylesheet if Webpack is being used for styles.
370
-
371
- #### Encapsulating sidecar assets
372
-
373
- Ideally, sidecar Javascript/CSS should not "leak" out of the context of its associated component.
374
-
375
- One approach is to use Web Components, which contain all Javascript functionality, internal markup, and styles within the shadow root of the Web Component.
330
+ ### Using helpers
376
331
 
377
- For example:
332
+ Helper methods can be used through the `helpers` proxy:
378
333
 
379
- `app/components/comment_component.rb`
380
334
  ```ruby
381
- class CommentComponent < ViewComponent::Base
382
- def initialize(comment:)
383
- @comment = comment
384
- end
385
-
386
- def commenter
387
- @comment.user
388
- end
389
-
390
- def commenter_name
391
- commenter.name
392
- end
393
-
394
- def avatar
395
- commenter.avatar_image_url
335
+ module IconHelper
336
+ def icon(name)
337
+ tag.i data: { feather: name.to_s.dasherize }
396
338
  end
339
+ end
397
340
 
398
- def formatted_body
399
- simple_format(@comment.body)
341
+ class UserComponent < ViewComponent::Base
342
+ def profile_icon
343
+ helpers.icon :user
400
344
  end
401
-
402
- private
403
-
404
- attr_reader :comment
405
345
  end
406
346
  ```
407
347
 
408
- `app/components/comment_component.html.erb`
409
- ```erb
410
- <my-comment comment-id="<%= comment.id %>">
411
- <time slot="posted" datetime="<%= comment.created_at.iso8601 %>"><%= comment.created_at.strftime("%b %-d") %></time>
412
-
413
- <div slot="avatar"><img src="<%= avatar %>" /></div>
414
-
415
- <div slot="author"><%= commenter_name %></div>
416
-
417
- <div slot="body"><%= formatted_body %></div>
418
- </my-comment>
419
- ```
348
+ Which can be used with `delegate`:
420
349
 
421
- `app/components/comment_component.js`
422
- ```js
423
- class Comment extends HTMLElement {
424
- styles() {
425
- return `
426
- :host {
427
- display: block;
428
- }
429
- ::slotted(time) {
430
- float: right;
431
- font-size: 0.75em;
432
- }
433
- .commenter { font-weight: bold; }
434
- .body { … }
435
- `
436
- }
350
+ ```ruby
351
+ class UserComponent < ViewComponent::Base
352
+ delegate :icon, to: :helpers
437
353
 
438
- constructor() {
439
- super()
440
- const shadow = this.attachShadow({mode: 'open'});
441
- shadow.innerHTML = `
442
- <style>
443
- ${this.styles()}
444
- </style>
445
- <slot name="posted"></slot>
446
- <div class="commenter">
447
- <slot name="avatar"></slot> <slot name="author"></slot>
448
- </div>
449
- <div class="body">
450
- <slot name="body"></slot>
451
- </div>
452
- `
453
- }
454
- }
455
- customElements.define('my-comment', Comment)
354
+ def profile_icon
355
+ icon :user
356
+ end
357
+ end
456
358
  ```
457
359
 
458
- ##### Stimulus
360
+ Helpers can also be used by including the helper:
459
361
 
460
- In Stimulus, create a 1:1 mapping between a Stimulus controller and a component. In order to load in Stimulus controllers from the `app/components` tree, amend the Stimulus boot code in `app/javascript/packs/application.js`:
362
+ ```ruby
363
+ class UserComponent < ViewComponent::Base
364
+ include IconHelper
461
365
 
462
- ```js
463
- const application = Application.start()
464
- const context = require.context("controllers", true, /.js$/)
465
- const context_components = require.context("../../components", true, /_controller.js$/)
466
- application.load(
467
- definitionsFromContext(context).concat(
468
- definitionsFromContext(context_components)
469
- )
470
- )
366
+ def profile_icon
367
+ icon :user
368
+ end
369
+ end
471
370
  ```
472
371
 
473
- This will allow you to create files such as `app/components/widget_controller.js`, where the controller identifier matches the `data-controller` attribute in the component's HTML template.
474
-
475
372
  ### Testing
476
373
 
477
- Unit test components directly, using the `render_inline` test helper. If you have a `capybara` test dependency, Capybara matchers will be available in your tests:
374
+ Unit test components directly, using the `render_inline` test helper, asserting against the rendered output.
375
+
376
+ Capybara matchers are available if the gem is installed:
478
377
 
479
378
  ```ruby
480
379
  require "view_component/test_case"
@@ -483,12 +382,12 @@ class MyComponentTest < ViewComponent::TestCase
483
382
  test "render component" do
484
383
  render_inline(TestComponent.new(title: "my title")) { "Hello, World!" }
485
384
 
486
- assert_selector("span[title='my title']", "Hello, World!")
385
+ assert_selector("span[title='my title']", text: "Hello, World!")
487
386
  end
488
387
  end
489
388
  ```
490
389
 
491
- In the absence of `capybara`, you can make assertions on the `render_inline` return value, which is an instance of `Nokogiri::HTML::DocumentFragment`:
390
+ In the absence of `capybara`, assertion against the return values of `render_inline`, which is an instance of `Nokogiri::HTML::DocumentFragment`:
492
391
 
493
392
  ```ruby
494
393
  test "render component" do
@@ -507,7 +406,7 @@ test "render component for tablet" do
507
406
  with_variant :tablet do
508
407
  render_inline(TestComponent.new(title: "my title")) { "Hello, tablets!" }
509
408
 
510
- assert_selector("span[title='my title']", "Hello, tablets!")
409
+ assert_selector("span[title='my title']", text: "Hello, tablets!")
511
410
  end
512
411
  end
513
412
  ```
@@ -551,17 +450,13 @@ class TestComponentPreview < ViewComponent::Preview
551
450
  end
552
451
  ```
553
452
 
554
- You'll then be able to pass down a value with <http://localhost:3000/rails/components/test_component/with_dynamic_title?title=Custom+title>.
453
+ Which enables passing in a value with <http://localhost:3000/rails/components/test_component/with_dynamic_title?title=Custom+title>.
555
454
 
556
455
  The `ViewComponent::Preview` base class includes
557
- [`ActionView::Helpers::TagHelper`][tag-helper], which provides the [`tag`][tag]
558
- and [`content_tag`][content_tag] view helper methods.
559
-
560
- [tag-helper]: https://api.rubyonrails.org/classes/ActionView/Helpers/TagHelper.html
561
- [tag]: https://api.rubyonrails.org/classes/ActionView/Helpers/TagHelper.html#method-i-tag
562
- [content_tag]: https://api.rubyonrails.org/classes/ActionView/Helpers/TagHelper.html#method-i-content_tag
456
+ [`ActionView::Helpers::TagHelper`](https://api.rubyonrails.org/classes/ActionView/Helpers/TagHelper.html), which provides the [`tag`](https://api.rubyonrails.org/classes/ActionView/Helpers/TagHelper.html#method-i-tag)
457
+ and [`content_tag`](https://api.rubyonrails.org/classes/ActionView/Helpers/TagHelper.html#method-i-content_tag) view helper methods.
563
458
 
564
- Previews default to the application layout, but can be overridden:
459
+ Previews use the application layout by default, but can use a specific layout with the `layout` option:
565
460
 
566
461
  `test/components/previews/test_component_preview.rb`
567
462
  ```ruby
@@ -572,18 +467,25 @@ class TestComponentPreview < ViewComponent::Preview
572
467
  end
573
468
  ```
574
469
 
575
- Preview classes live in `test/components/previews`, can be configured using the `preview_path` option.
576
-
577
- To use `lib/component_previews`:
470
+ Preview classes live in `test/components/previews`, which can be configured using the `preview_path` option:
578
471
 
579
472
  `config/application.rb`
580
473
  ```ruby
581
474
  config.view_component.preview_path = "#{Rails.root}/lib/component_previews"
582
475
  ```
583
476
 
477
+ Previews are served from <http://localhost:3000/rails/view_components> by default. To use a different endpoint, set the `preview_route` option:
478
+
479
+ `config/application.rb`
480
+ ```ruby
481
+ config.view_component.preview_route = "/previews"
482
+ ```
483
+
484
+ This example will make the previews available from <http://localhost:3000/previews>.
485
+
584
486
  #### Configuring TestController
585
487
 
586
- Component tests and previews assume the existence of an `ApplicationController` class, be can be configured using the `test_controller` option:
488
+ Component tests and previews assume the existence of an `ApplicationController` class, which be can be configured using the `test_controller` option:
587
489
 
588
490
  `config/application.rb`
589
491
  ```ruby
@@ -612,19 +514,144 @@ To use component previews:
612
514
  config.view_component.preview_path = "#{Rails.root}/spec/components/previews"
613
515
  ```
614
516
 
615
- ## Frequently Asked Questions
517
+ ### Sidecar assets (experimental)
616
518
 
617
- ### Can I use other templating languages besides ERB?
519
+ It’s possible to include Javascript and CSS alongside components, sometimes called "sidecar" assets or files.
520
+
521
+ To use the Webpacker gem to compile sidecar assets located in `app/components`:
522
+
523
+ 1. In `config/webpacker.yml`, add `"app/components"` to the `resolved_paths` array (e.g. `resolved_paths: ["app/components"]`).
524
+ 2. In the Webpack entry file (often `app/javascript/packs/application.js`), add an import statement to a helper file, and in the helper file, import the components' Javascript:
525
+
526
+ ```js
527
+ import "../components"
528
+ ```
529
+
530
+ Then, in `app/javascript/components.js`, add:
531
+
532
+ ```js
533
+ function importAll(r) {
534
+ r.keys().forEach(r)
535
+ }
536
+
537
+ importAll(require.context("../components", true, /_component.js$/))
538
+ ```
539
+
540
+ Any file with the `_component.js` suffix (such as `app/components/widget_component.js`) will be compiled into the Webpack bundle. If that file itself imports another file, for example `app/components/widget_component.css`, it will also be compiled and bundled into Webpack's output stylesheet if Webpack is being used for styles.
541
+
542
+ #### Encapsulating sidecar assets
543
+
544
+ Ideally, sidecar Javascript/CSS should not "leak" out of the context of its associated component.
545
+
546
+ One approach is to use Web Components, which contain all Javascript functionality, internal markup, and styles within the shadow root of the Web Component.
547
+
548
+ For example:
549
+
550
+ `app/components/comment_component.rb`
551
+ ```ruby
552
+ class CommentComponent < ViewComponent::Base
553
+ def initialize(comment:)
554
+ @comment = comment
555
+ end
556
+
557
+ def commenter
558
+ @comment.user
559
+ end
560
+
561
+ def commenter_name
562
+ commenter.name
563
+ end
564
+
565
+ def avatar
566
+ commenter.avatar_image_url
567
+ end
568
+
569
+ def formatted_body
570
+ simple_format(@comment.body)
571
+ end
572
+
573
+ private
618
574
 
619
- Yes. This gem is tested against ERB, Haml, and Slim, but it should support most Rails template handlers.
575
+ attr_reader :comment
576
+ end
577
+ ```
578
+
579
+ `app/components/comment_component.html.erb`
580
+ ```erb
581
+ <my-comment comment-id="<%= comment.id %>">
582
+ <time slot="posted" datetime="<%= comment.created_at.iso8601 %>"><%= comment.created_at.strftime("%b %-d") %></time>
583
+
584
+ <div slot="avatar"><img src="<%= avatar %>" /></div>
585
+
586
+ <div slot="author"><%= commenter_name %></div>
587
+
588
+ <div slot="body"><%= formatted_body %></div>
589
+ </my-comment>
590
+ ```
591
+
592
+ `app/components/comment_component.js`
593
+ ```js
594
+ class Comment extends HTMLElement {
595
+ styles() {
596
+ return `
597
+ :host {
598
+ display: block;
599
+ }
600
+ ::slotted(time) {
601
+ float: right;
602
+ font-size: 0.75em;
603
+ }
604
+ .commenter { font-weight: bold; }
605
+ .body { … }
606
+ `
607
+ }
608
+
609
+ constructor() {
610
+ super()
611
+ const shadow = this.attachShadow({mode: 'open'});
612
+ shadow.innerHTML = `
613
+ <style>
614
+ ${this.styles()}
615
+ </style>
616
+ <slot name="posted"></slot>
617
+ <div class="commenter">
618
+ <slot name="avatar"></slot> <slot name="author"></slot>
619
+ </div>
620
+ <div class="body">
621
+ <slot name="body"></slot>
622
+ </div>
623
+ `
624
+ }
625
+ }
626
+ customElements.define('my-comment', Comment)
627
+ ```
628
+
629
+ ##### Stimulus
630
+
631
+ In Stimulus, create a 1:1 mapping between a Stimulus controller and a component. In order to load in Stimulus controllers from the `app/components` tree, amend the Stimulus boot code in `app/javascript/packs/application.js`:
632
+
633
+ ```js
634
+ const application = Application.start()
635
+ const context = require.context("controllers", true, /.js$/)
636
+ const context_components = require.context("../../components", true, /_controller.js$/)
637
+ application.load(
638
+ definitionsFromContext(context).concat(
639
+ definitionsFromContext(context_components)
640
+ )
641
+ )
642
+ ```
620
643
 
621
- ### What happened to inline templates?
644
+ This enables the creation of files such as `app/components/widget_controller.js`, where the controller identifier matches the `data-controller` attribute in the component's HTML template.
645
+
646
+ ## Frequently Asked Questions
647
+
648
+ ### Can I use other templating languages besides ERB?
622
649
 
623
- Inline templates have been removed (for now) due to concerns raised by [@soutaro](https://github.com/soutaro) regarding compatibility with the type systems being developed for Ruby 3.
650
+ Yes. ViewComponent is tested against ERB, Haml, and Slim, but it should support most Rails template handlers.
624
651
 
625
652
  ### Isn't this just like X library?
626
653
 
627
- `ViewComponent` is far from a novel idea! Popular implementations of view components in Ruby include, but are not limited to:
654
+ ViewComponent is far from a novel idea! Popular implementations of view components in Ruby include, but are not limited to:
628
655
 
629
656
  - [trailblazer/cells](https://github.com/trailblazer/cells)
630
657
  - [dry-rb/dry-view](https://github.com/dry-rb/dry-view)
@@ -633,6 +660,7 @@ Inline templates have been removed (for now) due to concerns raised by [@soutaro
633
660
 
634
661
  ## Resources
635
662
 
663
+ - [Encapsulating Views, RailsConf 2020](https://youtu.be/YVYRus_2KZM)
636
664
  - [ViewComponent at GitHub with Joel Hawksley](https://the-ruby-blend.fireside.fm/9)
637
665
  - [Components, HAML vs ERB, and Design Systems](https://the-ruby-blend.fireside.fm/4)
638
666
  - [Choosing the Right Tech Stack with Dave Paola](https://5by5.tv/rubyonrails/307)
@@ -648,7 +676,7 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/github
648
676
 
649
677
  ## Contributors
650
678
 
651
- `view_component` is built by:
679
+ ViewComponent is built by:
652
680
 
653
681
  |<img src="https://avatars.githubusercontent.com/joelhawksley?s=256" alt="joelhawksley" width="128" />|<img src="https://avatars.githubusercontent.com/tenderlove?s=256" alt="tenderlove" width="128" />|<img src="https://avatars.githubusercontent.com/jonspalmer?s=256" alt="jonspalmer" width="128" />|<img src="https://avatars.githubusercontent.com/juanmanuelramallo?s=256" alt="juanmanuelramallo" width="128" />|<img src="https://avatars.githubusercontent.com/vinistock?s=256" alt="vinistock" width="128" />|
654
682
  |:---:|:---:|:---:|:---:|:---:|
@@ -687,4 +715,4 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/github
687
715
 
688
716
  ## License
689
717
 
690
- The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
718
+ ViewComponent is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -2,8 +2,8 @@
2
2
 
3
3
  require "rails/application_controller"
4
4
 
5
- class Rails::ViewComponentsController < Rails::ApplicationController # :nodoc:
6
- prepend_view_path File.expand_path("../../../lib/railties/lib/rails/templates/rails", __dir__)
5
+ class ViewComponentsController < Rails::ApplicationController # :nodoc:
6
+ prepend_view_path File.expand_path("../views", __dir__)
7
7
 
8
8
  around_action :set_locale, only: :previews
9
9
  before_action :find_preview, only: :previews
@@ -16,26 +16,19 @@ class Rails::ViewComponentsController < Rails::ApplicationController # :nodoc:
16
16
  def index
17
17
  @previews = ViewComponent::Preview.all
18
18
  @page_title = "Component Previews"
19
- # rubocop:disable GitHub/RailsControllerRenderPathsExist
20
- render "components/index"
21
- # rubocop:enable GitHub/RailsControllerRenderPathsExist
22
19
  end
23
20
 
24
21
  def previews
25
22
  if params[:path] == @preview.preview_name
26
23
  @page_title = "Component Previews for #{@preview.preview_name}"
27
- # rubocop:disable GitHub/RailsControllerRenderPathsExist
28
- render "components/previews"
29
- # rubocop:enable GitHub/RailsControllerRenderPathsExist
24
+ render "view_components/previews"
30
25
  else
31
26
  prepend_application_view_paths
32
27
  @example_name = File.basename(params[:path])
33
28
  @render_args = @preview.render_args(@example_name, params: params.permit!)
34
29
  layout = @render_args[:layout]
35
30
  opts = layout.nil? ? {} : { layout: layout }
36
- # rubocop:disable GitHub/RailsControllerRenderPathsExist
37
- render "components/preview", **opts
38
- # rubocop:enable GitHub/RailsControllerRenderPathsExist
31
+ render "view_components/preview", opts
39
32
  end
40
33
  end
41
34
 
@@ -0,0 +1,8 @@
1
+ <% @previews.each do |preview| %>
2
+ <h3><%= link_to preview.preview_name.titleize, preview_view_component_path(preview.preview_name) %></h3>
3
+ <ul>
4
+ <% preview.examples.each do |preview_example| %>
5
+ <li><%= link_to preview_example, preview_view_component_path("#{preview.preview_name}/#{preview_example}") %></li>
6
+ <% end %>
7
+ </ul>
8
+ <% end %>
@@ -0,0 +1 @@
1
+ <%= render(@render_args[:component], @render_args[:args], &@render_args[:block])%>
@@ -1,6 +1,6 @@
1
1
  <h3><%= @preview.preview_name.titleize %></h3>
2
2
  <ul>
3
3
  <% @preview.examples.each do |example| %>
4
- <li><%= link_to example, "/rails/view_components/#{@preview.preview_name}/#{example}" %></li>
4
+ <li><%= link_to example, preview_view_component_path("#{@preview.preview_name}/#{example}") %></li>
5
5
  <% end %>
6
6
  </ul>
@@ -10,15 +10,11 @@ module ViewComponent
10
10
  include ActiveSupport::Configurable
11
11
  include ViewComponent::Previewable
12
12
 
13
+ # For CSRF authenticity tokens in forms
13
14
  delegate :form_authenticity_token, :protect_against_forgery?, to: :helpers
14
15
 
15
- class_attribute :content_areas, default: []
16
- self.content_areas = [] # default doesn't work until Rails 5.2
17
-
18
- # Render a component collection.
19
- def self.with_collection(*args)
20
- Collection.new(self, *args)
21
- end
16
+ class_attribute :content_areas
17
+ self.content_areas = [] # class_attribute:default doesn't work until Rails 5.2
22
18
 
23
19
  # Entrypoint for rendering components.
24
20
  #
@@ -45,17 +41,29 @@ module ViewComponent
45
41
  # <span title="greeting">Hello, world!</span>
46
42
  #
47
43
  def render_in(view_context, &block)
48
- self.class.compile!
44
+ self.class.compile(raise_errors: true)
45
+
49
46
  @view_context = view_context
50
- @view_renderer ||= view_context.view_renderer
51
47
  @lookup_context ||= view_context.lookup_context
48
+
49
+ # required for path helpers in older Rails versions
50
+ @view_renderer ||= view_context.view_renderer
51
+
52
+ # For content_for
52
53
  @view_flow ||= view_context.view_flow
54
+
55
+ # For i18n
53
56
  @virtual_path ||= virtual_path
57
+
58
+ # For template variants (+phone, +desktop, etc.)
54
59
  @variant = @lookup_context.variants.first
55
60
 
61
+ # For caching, such as #cache_if
62
+ @current_template = nil unless defined?(@current_template)
56
63
  old_current_template = @current_template
57
64
  @current_template = self
58
65
 
66
+ # Assign captured content passed to component as a block to @content
59
67
  @content = view_context.capture(self, &block) if block_given?
60
68
 
61
69
  before_render_check
@@ -77,12 +85,10 @@ module ViewComponent
77
85
  true
78
86
  end
79
87
 
80
- def self.short_identifier
81
- @short_identifier ||= defined?(Rails.root) ? source_location.sub("#{Rails.root}/", "") : source_location
82
- end
83
-
84
88
  def initialize(*); end
85
89
 
90
+ # If trying to render a partial or template inside a component,
91
+ # pass the render call to the parent view_context.
86
92
  def render(options = {}, args = {}, &block)
87
93
  if options.is_a?(String) || (options.is_a?(Hash) && options.has_key?(:partial))
88
94
  view_context.render(options, args, &block)
@@ -95,7 +101,7 @@ module ViewComponent
95
101
  @controller ||= view_context.controller
96
102
  end
97
103
 
98
- # Provides a proxy to access helper methods through
104
+ # Provides a proxy to access helper methods
99
105
  def helpers
100
106
  @helpers ||= view_context
101
107
  end
@@ -105,14 +111,17 @@ module ViewComponent
105
111
  self.class.source_location.gsub(%r{(.*app/components)|(\.rb)}, "")
106
112
  end
107
113
 
114
+ # For caching, such as #cache_if
108
115
  def view_cache_dependencies
109
116
  []
110
117
  end
111
118
 
112
- def format # :nodoc:
119
+ # For caching, such as #cache_if
120
+ def format
113
121
  @variant
114
122
  end
115
123
 
124
+ # Assign the provided content to the content area accessor
116
125
  def with(area, content = nil, &block)
117
126
  unless content_areas.include?(area)
118
127
  raise ArgumentError.new "Unknown content_area '#{area}' - expected one of '#{content_areas}'"
@@ -128,6 +137,9 @@ module ViewComponent
128
137
 
129
138
  private
130
139
 
140
+ # Exposes the current request to the component.
141
+ # Use sparingly as doing so introduces coupling
142
+ # that inhibits encapsulation & reuse.
131
143
  def request
132
144
  @request ||= controller.request
133
145
  end
@@ -143,7 +155,18 @@ module ViewComponent
143
155
  class << self
144
156
  attr_accessor :source_location
145
157
 
158
+ # Render a component collection.
159
+ def with_collection(*args)
160
+ Collection.new(self, *args)
161
+ end
162
+
163
+ # Provide identifier for ActionView template annotations
164
+ def short_identifier
165
+ @short_identifier ||= defined?(Rails.root) ? source_location.sub("#{Rails.root}/", "") : source_location
166
+ end
167
+
146
168
  def inherited(child)
169
+ # If we're in Rails, add application url_helpers to the component context
147
170
  if defined?(Rails)
148
171
  child.include Rails.application.routes.url_helpers unless child < Rails.application.routes.url_helpers
149
172
  end
@@ -165,36 +188,52 @@ module ViewComponent
165
188
  end
166
189
 
167
190
  def compiled?
168
- @compiled && ActionView::Base.cache_template_loading
169
- end
191
+ @compiled ||= false
170
192
 
171
- def compile!
172
- compile(raise_template_errors: true)
193
+ @compiled && ActionView::Base.cache_template_loading
173
194
  end
174
195
 
175
196
  # Compile templates to instance methods, assuming they haven't been compiled already.
176
- # We could in theory do this on app boot, at least in production environments.
177
- # Right now this just compiles the first time the component is rendered.
178
- def compile(raise_template_errors: false)
197
+ #
198
+ # Do as much work as possible in this step, as doing so reduces the amount
199
+ # of work done each time a component is rendered.
200
+ def compile(raise_errors: false)
179
201
  return if compiled?
180
202
 
181
203
  if template_errors.present?
182
- raise ViewComponent::TemplateError.new(template_errors) if raise_template_errors
204
+ raise ViewComponent::TemplateError.new(template_errors) if raise_errors
183
205
  return false
184
206
  end
185
207
 
208
+ # Remove any existing singleton methods,
209
+ # as Ruby warns when redefining a method.
210
+ remove_possible_singleton_method(:variants)
211
+ remove_possible_singleton_method(:collection_parameter)
212
+ remove_possible_singleton_method(:collection_counter_parameter)
213
+ remove_possible_singleton_method(:counter_argument_present?)
214
+
186
215
  define_singleton_method(:variants) do
187
216
  templates.map { |template| template[:variant] } + variants_from_inline_calls(inline_calls)
188
217
  end
189
218
 
190
- define_singleton_method(:collection_counter_parameter_name) do
191
- "#{collection_parameter_name}_counter".to_sym
219
+ define_singleton_method(:collection_parameter) do
220
+ if provided_collection_parameter
221
+ provided_collection_parameter
222
+ else
223
+ name.demodulize.underscore.chomp("_component").to_sym
224
+ end
225
+ end
226
+
227
+ define_singleton_method(:collection_counter_parameter) do
228
+ "#{collection_parameter}_counter".to_sym
192
229
  end
193
230
 
194
231
  define_singleton_method(:counter_argument_present?) do
195
- instance_method(:initialize).parameters.map(&:second).include?(collection_counter_parameter_name)
232
+ instance_method(:initialize).parameters.map(&:second).include?(collection_counter_parameter)
196
233
  end
197
234
 
235
+ validate_collection_parameter! if raise_errors
236
+
198
237
  # If template name annotations are turned on, a line is dynamically
199
238
  # added with a comment. In this case, we want to return a different
200
239
  # starting line number so errors that are raised will point to the
@@ -208,8 +247,13 @@ module ViewComponent
208
247
  end
209
248
 
210
249
  templates.each do |template|
250
+ # Remove existing compiled template methods,
251
+ # as Ruby warns when redefining a method.
252
+ method_name = call_method_name(template[:variant])
253
+ undef_method(method_name.to_sym) if instance_methods.include?(method_name.to_sym)
254
+
211
255
  class_eval <<-RUBY, template[:path], line_number
212
- def #{call_method_name(template[:variant])}
256
+ def #{method_name}
213
257
  @output_buffer = ActionView::OutputBuffer.new
214
258
  #{compiled_template(template[:path])}
215
259
  end
@@ -236,21 +280,38 @@ module ViewComponent
236
280
  if areas.include?(:content)
237
281
  raise ArgumentError.new ":content is a reserved content area name. Please use another name, such as ':body'"
238
282
  end
239
- attr_reader *areas
283
+ attr_reader(*areas)
240
284
  self.content_areas = areas
241
285
  end
242
286
 
243
- # Support overriding this component's collection parameter name
287
+ # Support overriding collection parameter name
244
288
  def with_collection_parameter(param)
245
- @with_collection_parameter = param
289
+ @provided_collection_parameter = param
246
290
  end
247
291
 
248
- def collection_parameter_name
249
- (@with_collection_parameter || name.demodulize.underscore.chomp("_component")).to_sym
292
+ # Ensure the component initializer accepts the
293
+ # collection parameter. By default, we do not
294
+ # validate that the default parameter name
295
+ # is accepted, as support for collection
296
+ # rendering is optional.
297
+ def validate_collection_parameter!(validate_default: false)
298
+ parameter = validate_default ? collection_parameter : provided_collection_parameter
299
+
300
+ return unless parameter
301
+ return if instance_method(:initialize).parameters.map(&:last).include?(parameter)
302
+
303
+ raise ArgumentError.new(
304
+ "#{self} initializer must accept " \
305
+ "`#{parameter}` collection parameter."
306
+ )
250
307
  end
251
308
 
252
309
  private
253
310
 
311
+ def provided_collection_parameter
312
+ @provided_collection_parameter ||= nil
313
+ end
314
+
254
315
  def compiled_template(file_path)
255
316
  handler = ActionView::Template.handler_for_extension(File.extname(file_path).gsub(".", ""))
256
317
  template = File.read(file_path)
@@ -5,7 +5,9 @@ module ViewComponent
5
5
  def render_in(view_context, &block)
6
6
  iterator = ActionView::PartialIteration.new(@collection.size)
7
7
 
8
- @component.compile!
8
+ @component.compile(raise_errors: true)
9
+ @component.validate_collection_parameter!(validate_default: true)
10
+
9
11
  @collection.map do |item|
10
12
  content = @component.new(component_options(item, iterator)).render_in(view_context, &block)
11
13
  iterator.iterate!
@@ -30,8 +32,8 @@ module ViewComponent
30
32
  end
31
33
 
32
34
  def component_options(item, iterator)
33
- item_options = { @component.collection_parameter_name => item }
34
- item_options[@component.collection_counter_parameter_name] = iterator.index + 1 if @component.counter_argument_present?
35
+ item_options = { @component.collection_parameter => item }
36
+ item_options[@component.collection_counter_parameter] = iterator.index + 1 if @component.counter_argument_present?
35
37
 
36
38
  @options.merge(item_options)
37
39
  end
@@ -11,6 +11,7 @@ module ViewComponent
11
11
  options = app.config.view_component
12
12
 
13
13
  options.show_previews = Rails.env.development? if options.show_previews.nil?
14
+ options.preview_route ||= ViewComponent::Base.preview_route
14
15
 
15
16
  if options.show_previews
16
17
  options.preview_path ||= defined?(Rails.root) ? "#{Rails.root}/test/components/previews" : nil
@@ -64,8 +65,8 @@ module ViewComponent
64
65
 
65
66
  if options.show_previews
66
67
  app.routes.prepend do
67
- get "/rails/view_components" => "rails/view_components#index", :internal => true
68
- get "/rails/view_components/*path" => "rails/view_components#previews", :internal => true
68
+ get options.preview_route, to: "view_components#index", as: :preview_view_components, internal: true
69
+ get "#{options.preview_route}/*path", to: "view_components#previews", as: :preview_view_component, internal: true
69
70
  end
70
71
  end
71
72
  end
@@ -23,6 +23,7 @@ module ViewComponent # :nodoc:
23
23
  example_params_names = instance_method(example).parameters.map(&:last)
24
24
  provided_params = params.slice(*example_params_names).to_h.symbolize_keys
25
25
  result = provided_params.empty? ? new.public_send(example) : new.public_send(example, **provided_params)
26
+ @layout = nil unless defined?(@layout)
26
27
  result.merge(layout: @layout)
27
28
  end
28
29
 
@@ -20,6 +20,16 @@ module ViewComponent # :nodoc:
20
20
  # Defaults to +true+ for development environment
21
21
  #
22
22
  mattr_accessor :show_previews, instance_writer: false
23
+
24
+ # Set the entry route for component previews through app configuration:
25
+ #
26
+ # config.view_component.preview_route = "/previews"
27
+ #
28
+ # Defaults to +/rails/view_components+ when `show_previews' is enabled
29
+ #
30
+ mattr_accessor :preview_route, instance_writer: false do
31
+ "/rails/view_components"
32
+ end
23
33
  end
24
34
  end
25
35
  end
@@ -3,8 +3,8 @@
3
3
  module ViewComponent
4
4
  module VERSION
5
5
  MAJOR = 2
6
- MINOR = 5
7
- PATCH = 1
6
+ MINOR = 6
7
+ PATCH = 0
8
8
 
9
9
  STRING = [MAJOR, MINOR, PATCH].join(".")
10
10
  end
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.5.1
4
+ version: 2.6.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-04-21 00:00:00.000000000 Z
11
+ date: 2020-05-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -64,14 +64,14 @@ dependencies:
64
64
  requirements:
65
65
  - - '='
66
66
  - !ruby/object:Gem::Version
67
- version: 5.1.0
67
+ version: 5.6.0
68
68
  type: :development
69
69
  prerelease: false
70
70
  version_requirements: !ruby/object:Gem::Requirement
71
71
  requirements:
72
72
  - - '='
73
73
  - !ruby/object:Gem::Version
74
- version: 5.1.0
74
+ version: 5.6.0
75
75
  - !ruby/object:Gem::Dependency
76
76
  name: haml
77
77
  requirement: !ruby/object:Gem::Requirement
@@ -152,7 +152,10 @@ files:
152
152
  - CHANGELOG.md
153
153
  - LICENSE.txt
154
154
  - README.md
155
- - app/controllers/rails/view_components_controller.rb
155
+ - app/controllers/view_components_controller.rb
156
+ - app/views/view_components/index.html.erb
157
+ - app/views/view_components/preview.html.erb
158
+ - app/views/view_components/previews.html.erb
156
159
  - lib/rails/generators/component/USAGE
157
160
  - lib/rails/generators/component/component_generator.rb
158
161
  - lib/rails/generators/component/templates/component.rb.tt
@@ -166,10 +169,6 @@ files:
166
169
  - lib/rails/generators/slim/templates/component.html.slim.tt
167
170
  - lib/rails/generators/test_unit/component_generator.rb
168
171
  - lib/rails/generators/test_unit/templates/component_test.rb.tt
169
- - lib/railties/lib/rails.rb
170
- - lib/railties/lib/rails/templates/rails/components/index.html.erb
171
- - lib/railties/lib/rails/templates/rails/components/preview.html.erb
172
- - lib/railties/lib/rails/templates/rails/components/previews.html.erb
173
172
  - lib/view_component.rb
174
173
  - lib/view_component/base.rb
175
174
  - lib/view_component/collection.rb
@@ -1,5 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Rails
4
- autoload :ComponentsController
5
- end
@@ -1,8 +0,0 @@
1
- <% @previews.each do |preview| %>
2
- <h3><%= link_to preview.preview_name.titleize, "/rails/view_components/#{preview.preview_name}" %></h3>
3
- <ul>
4
- <% preview.examples.each do |preview_example| %>
5
- <li><%= link_to preview_example, "/rails/view_components/#{preview.preview_name}/#{preview_example}" %></li>
6
- <% end %>
7
- </ul>
8
- <% end %>
@@ -1 +0,0 @@
1
- <%= render(@render_args[:component], **@render_args[:args], &@render_args[:block])%>