actionview-component 1.14.1 → 1.15.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/PULL_REQUEST_TEMPLATE +1 -1
- data/CHANGELOG.md +24 -0
- data/CONTRIBUTING.md +5 -5
- data/Gemfile.lock +1 -1
- data/README.md +82 -210
- data/actionview-component.gemspec +3 -3
- data/docs/case-studies/jellyswitch.md +76 -0
- data/lib/action_view/component/test_case.rb +2 -1
- data/lib/action_view/component/test_helpers.rb +17 -0
- data/lib/view_component.rb +1 -0
- data/lib/view_component/preview.rb +1 -0
- data/lib/view_component/render_monkey_patch.rb +1 -1
- data/lib/view_component/rendering_monkey_patch.rb +1 -1
- data/lib/view_component/test_helpers.rb +0 -8
- data/lib/view_component/version.rb +2 -2
- metadata +7 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 38891bd3b2a3b00b7da81feb7e78fa55e5efbbf5f4bcf25abd47cf68893e7725
|
4
|
+
data.tar.gz: d53bc1d826b6504ea9b4fd4d5aa91362995fec79e12471c12466ea62a31c59b6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3b3c2bfa37f043f19b52d9dcca5e07c8ecd7cdc3c4e06edaf775eedf5b1ced6e1a32bec284116a39222f2e67249928a175f72a75d33e84f81eb00cc918f40015
|
7
|
+
data.tar.gz: 626cf782c789aef2e982f13a2f8f313a3f9bf37e0bcaefc1010e9ee4f70335a6fc6062a48ca7e2d49ef4c9f0a8f4f3a9a1ed493fb5440d40da5253bf9393fb86
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,29 @@
|
|
1
1
|
# master
|
2
2
|
|
3
|
+
# v1.15.0
|
4
|
+
|
5
|
+
* Re-introduce ActionView::Component::TestHelpers.
|
6
|
+
|
7
|
+
*Joel Hawksley*
|
8
|
+
|
9
|
+
* Bypass monkey patch on Rails 6.1 builds.
|
10
|
+
|
11
|
+
*Joel Hawksley*
|
12
|
+
|
13
|
+
* Make `ActionView::Helpers::TagHelper` available in Previews.
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
def with_html_content
|
17
|
+
render(MyComponent.new) do
|
18
|
+
tag.div do
|
19
|
+
content_tag(:span, "Hello")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
```
|
24
|
+
|
25
|
+
*Sean Doyle*
|
26
|
+
|
3
27
|
# v1.14.1
|
4
28
|
|
5
29
|
* Fix bug where generator created invalid test code.
|
data/CONTRIBUTING.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Contributing
|
2
2
|
|
3
|
-
[fork]: https://github.com/github/
|
4
|
-
[pr]: https://github.com/github/
|
3
|
+
[fork]: https://github.com/github/view_component/fork
|
4
|
+
[pr]: https://github.com/github/view_component/compare
|
5
5
|
[style]: https://github.com/styleguide/ruby
|
6
6
|
[code-of-conduct]: CODE_OF_CONDUCT.md
|
7
7
|
|
@@ -38,9 +38,9 @@ If you are the current maintainer of this gem:
|
|
38
38
|
1. Add version heading/entries to `CHANGELOG.md`.
|
39
39
|
1. Make sure your local dependencies are up to date: `bundle`
|
40
40
|
1. Ensure that tests are green: `bundle exec rake`
|
41
|
-
1. Make a PR to github/
|
41
|
+
1. Make a PR to github/view_component.
|
42
42
|
1. Build a local gem: `gem build actionview-component.gemspec`
|
43
|
-
1. Merge github/
|
43
|
+
1. Merge github/view_component PR
|
44
44
|
1. Tag and push: `git tag vx.xx.xx; git push --tags`
|
45
|
-
1. Create a GitHub release with the pushed tag (https://github.com/github/
|
45
|
+
1. Create a GitHub release with the pushed tag (https://github.com/github/view_component/releases/new) and populate it with a list of the commits from `git log --pretty=format:"- %s" --reverse refs/tags/[OLD TAG]...refs/tags/[NEW TAG]`
|
46
46
|
1. Push to rubygems.org -- `gem push actionview-component-VERSION.gem`
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# ViewComponent
|
2
2
|
A view component framework for Rails.
|
3
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/
|
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
5
|
|
6
6
|
## Migration in progress
|
7
7
|
|
8
|
-
This gem is in the process of a name / API change from `ActionView::Component` to `ViewComponent`, see https://github.com/github/
|
8
|
+
This gem is in the process of a name / API change from `ActionView::Component` to `ViewComponent`, see https://github.com/github/view_component/issues/206.
|
9
9
|
|
10
10
|
### What's changing in the migration
|
11
11
|
|
@@ -19,7 +19,7 @@ This gem is in the process of a name / API change from `ActionView::Component` t
|
|
19
19
|
1. Update components to inherit from `ViewComponent::Base`.
|
20
20
|
1. Update component tests to inherit from `ViewComponent::TestCase`.
|
21
21
|
1. Update component previews to inherit from `ViewComponent::Preview`.
|
22
|
-
1. Include `ViewComponent::TestHelpers` in
|
22
|
+
1. Include `ViewComponent::TestHelpers` in the appropriate test helper file.
|
23
23
|
|
24
24
|
## Roadmap
|
25
25
|
|
@@ -33,20 +33,16 @@ This library is designed to integrate as seamlessly as possible with Rails, with
|
|
33
33
|
|
34
34
|
## Compatibility
|
35
35
|
|
36
|
-
`actionview-component` is tested for compatibility with combinations of Ruby `2.5`/`2.6`/`2.7` and Rails `5.0.0`/`5.2.3`/`6.0.0`/`
|
36
|
+
`actionview-component` is tested for compatibility with combinations of Ruby `2.5`/`2.6`/`2.7` and Rails `5.0.0`/`5.2.3`/`6.0.0`/`master`.
|
37
37
|
|
38
38
|
## Installation
|
39
|
-
|
39
|
+
|
40
|
+
In `Gemfile`, add:
|
40
41
|
|
41
42
|
```ruby
|
42
43
|
gem "actionview-component"
|
43
44
|
```
|
44
45
|
|
45
|
-
And then execute:
|
46
|
-
```bash
|
47
|
-
$ bundle
|
48
|
-
```
|
49
|
-
|
50
46
|
In `config/application.rb`, add:
|
51
47
|
|
52
48
|
```bash
|
@@ -59,49 +55,41 @@ require "view_component/engine"
|
|
59
55
|
|
60
56
|
`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).
|
61
57
|
|
62
|
-
|
58
|
+
Components are most effective in cases where view code is reused or benefits from being tested directly.
|
63
59
|
|
64
|
-
|
60
|
+
### Why should I use components?
|
65
61
|
|
66
62
|
#### Testing
|
67
63
|
|
68
|
-
|
64
|
+
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.
|
69
65
|
|
70
|
-
|
66
|
+
For partials, this means being tested for each view they are included in, reducing the benefit of reusing them.
|
71
67
|
|
72
|
-
|
68
|
+
`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.
|
73
69
|
|
74
70
|
#### Data Flow
|
75
71
|
|
76
|
-
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
|
77
|
-
|
78
|
-
#### Standards
|
72
|
+
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.
|
79
73
|
|
80
|
-
|
74
|
+
By clearly defining the context necessary to render a `ViewComponent`, they're easier to reuse than partials.
|
81
75
|
|
82
|
-
|
76
|
+
#### Standards
|
83
77
|
|
84
|
-
|
78
|
+
Views often fail basic Ruby code quality standards: long methods, deep conditional nesting, and mystery guests abound.
|
85
79
|
|
86
|
-
`ViewComponent`
|
80
|
+
`ViewComponent`s are Ruby objects, making it easy to follow code quality standards.
|
87
81
|
|
88
82
|
#### Code Coverage
|
89
83
|
|
90
|
-
|
91
|
-
|
92
|
-
#### Data flow
|
93
|
-
|
94
|
-
By clearly defining the context necessary to render a component, we’ve found them to be easier to reuse than partials.
|
95
|
-
|
96
|
-
### When should I use components?
|
84
|
+
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.
|
97
85
|
|
98
|
-
|
86
|
+
`ViewComponent` is at least partially compatible with code coverage tools, such as SimpleCov.
|
99
87
|
|
100
88
|
### Building components
|
101
89
|
|
102
90
|
#### Conventions
|
103
91
|
|
104
|
-
Components are subclasses of `ViewComponent::Base` and live in `app/components`.
|
92
|
+
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.
|
105
93
|
|
106
94
|
Component class names end in -`Component`.
|
107
95
|
|
@@ -123,21 +111,17 @@ bin/rails generate component Example title content
|
|
123
111
|
create app/components/example_component.html.erb
|
124
112
|
```
|
125
113
|
|
126
|
-
`ViewComponent` includes template generators for the `erb`, `haml`, and `slim` template engines and will use the template engine specified in
|
114
|
+
`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.
|
127
115
|
|
128
|
-
|
116
|
+
The template engine can also be passed as an option to the generator:
|
129
117
|
|
130
118
|
```bash
|
131
119
|
bin/rails generate component Example title content --template-engine slim
|
132
|
-
invoke test_unit
|
133
|
-
create test/components/example_component_test.rb
|
134
|
-
create app/components/example_component.rb
|
135
|
-
create app/components/example_component.html.slim
|
136
120
|
```
|
137
121
|
|
138
122
|
#### Implementation
|
139
123
|
|
140
|
-
A `ViewComponent` is a Ruby file and corresponding template file
|
124
|
+
A `ViewComponent` is a Ruby file and corresponding template file with the same base name:
|
141
125
|
|
142
126
|
`app/components/test_component.rb`:
|
143
127
|
```ruby
|
@@ -145,19 +129,15 @@ class TestComponent < ViewComponent::Base
|
|
145
129
|
def initialize(title:)
|
146
130
|
@title = title
|
147
131
|
end
|
148
|
-
|
149
|
-
private
|
150
|
-
|
151
|
-
attr_reader :title
|
152
132
|
end
|
153
133
|
```
|
154
134
|
|
155
135
|
`app/components/test_component.html.erb`:
|
156
136
|
```erb
|
157
|
-
<span title="<%= title %>"><%= content %></span>
|
137
|
+
<span title="<%= @title %>"><%= content %></span>
|
158
138
|
```
|
159
139
|
|
160
|
-
|
140
|
+
Which is rendered in a view as:
|
161
141
|
|
162
142
|
```erb
|
163
143
|
<%= render(TestComponent.new(title: "my title")) do %>
|
@@ -171,6 +151,8 @@ Which returns:
|
|
171
151
|
<span title="my title">Hello, World!</span>
|
172
152
|
```
|
173
153
|
|
154
|
+
`ViewComponent` requires the presence of an `initialize` method in each component.
|
155
|
+
|
174
156
|
#### Content Areas
|
175
157
|
|
176
158
|
A component can declare additional content areas to be rendered in the component. For example:
|
@@ -180,11 +162,8 @@ A component can declare additional content areas to be rendered in the component
|
|
180
162
|
class ModalComponent < ViewComponent::Base
|
181
163
|
with_content_areas :header, :body
|
182
164
|
|
183
|
-
def initialize(
|
184
|
-
@user = user
|
165
|
+
def initialize(*)
|
185
166
|
end
|
186
|
-
|
187
|
-
attr_reader :user
|
188
167
|
end
|
189
168
|
```
|
190
169
|
|
@@ -196,12 +175,12 @@ end
|
|
196
175
|
</div>
|
197
176
|
```
|
198
177
|
|
199
|
-
|
178
|
+
Which is rendered in a view as:
|
200
179
|
|
201
180
|
```erb
|
202
|
-
<%= render(ModalComponent.new
|
181
|
+
<%= render(ModalComponent.new) do |component| %>
|
203
182
|
<% component.with(:header) do %>
|
204
|
-
Hello
|
183
|
+
Hello Jane
|
205
184
|
<% end %>
|
206
185
|
<% component.with(:body) do %>
|
207
186
|
<p>Have a great day.</p>
|
@@ -218,128 +197,14 @@ Which returns:
|
|
218
197
|
</div>
|
219
198
|
```
|
220
199
|
|
221
|
-
Content for content areas can be passed as arguments to the render method or as named blocks passed to the `with` method.
|
222
|
-
This allows a few different combinations of ways to render the component:
|
223
|
-
|
224
|
-
##### Required render argument optionally overridden or wrapped by a named block
|
225
|
-
|
226
|
-
`app/components/modal_component.rb`:
|
227
|
-
```ruby
|
228
|
-
class ModalComponent < ViewComponent::Base
|
229
|
-
with_content_areas :header, :body
|
230
|
-
|
231
|
-
def initialize(header:)
|
232
|
-
@header = header
|
233
|
-
end
|
234
|
-
end
|
235
|
-
```
|
236
|
-
|
237
|
-
```erb
|
238
|
-
<%= render(ModalComponent.new(header: "Hi!")) do |component| %>
|
239
|
-
<% component.with(:header) do %>
|
240
|
-
<span class="help_icon"><%= component.header %></span>
|
241
|
-
<% end %>
|
242
|
-
<% component.with(:body) do %>
|
243
|
-
<p>Have a great day.</p>
|
244
|
-
<% end %>
|
245
|
-
<% end %>
|
246
|
-
```
|
247
|
-
|
248
|
-
##### Required argument passed by render argument or by named block
|
249
|
-
|
250
|
-
`app/components/modal_component.rb`:
|
251
|
-
```ruby
|
252
|
-
class ModalComponent < ViewComponent::Base
|
253
|
-
with_content_areas :header, :body
|
254
|
-
|
255
|
-
def initialize(header: nil)
|
256
|
-
@header = header
|
257
|
-
end
|
258
|
-
end
|
259
|
-
```
|
260
|
-
|
261
|
-
`app/views/render_arg.html.erb`:
|
262
|
-
```erb
|
263
|
-
<%= render(ModalComponent.new(header: "Hi!")) do |component| %>
|
264
|
-
<% component.with(:body) do %>
|
265
|
-
<p>Have a great day.</p>
|
266
|
-
<% end %>
|
267
|
-
<% end %>
|
268
|
-
```
|
269
|
-
|
270
|
-
`app/views/with_block.html.erb`:
|
271
|
-
```erb
|
272
|
-
<%= render(ModalComponent) do |component| %>
|
273
|
-
<% component.with(:header) do %>
|
274
|
-
<span class="help_icon">Hello</span>
|
275
|
-
<% end %>
|
276
|
-
<% component.with(:body) do %>
|
277
|
-
<p>Have a great day.</p>
|
278
|
-
<% end %>
|
279
|
-
<% end %>
|
280
|
-
```
|
281
|
-
|
282
|
-
##### Optional argument passed by render argument, by named block, or neither
|
283
|
-
|
284
|
-
`app/components/modal_component.rb`:
|
285
|
-
```ruby
|
286
|
-
class ModalComponent < ViewComponent::Base
|
287
|
-
with_content_areas :header, :body
|
288
|
-
|
289
|
-
def initialize(header: nil)
|
290
|
-
@header = header
|
291
|
-
end
|
292
|
-
end
|
293
|
-
```
|
294
|
-
|
295
|
-
`app/components/modal_component.html.erb`:
|
296
|
-
```erb
|
297
|
-
<div class="modal">
|
298
|
-
<% if header %>
|
299
|
-
<div class="header"><%= header %></div>
|
300
|
-
<% end %>
|
301
|
-
<div class="body"><%= body %></div>
|
302
|
-
</div>
|
303
|
-
```
|
304
|
-
|
305
|
-
`app/views/render_arg.html.erb`:
|
306
|
-
```erb
|
307
|
-
<%= render(ModalComponent.new(header: "Hi!")) do |component| %>
|
308
|
-
<% component.with(:body) do %>
|
309
|
-
<p>Have a great day.</p>
|
310
|
-
<% end %>
|
311
|
-
<% end %>
|
312
|
-
```
|
313
|
-
|
314
|
-
`app/views/with_block.html.erb`:
|
315
|
-
```erb
|
316
|
-
<%= render(ModalComponent.new) do |component| %>
|
317
|
-
<% component.with(:header) do %>
|
318
|
-
<span class="help_icon">Hello</span>
|
319
|
-
<% end %>
|
320
|
-
<% component.with(:body) do %>
|
321
|
-
<p>Have a great day.</p>
|
322
|
-
<% end %>
|
323
|
-
<% end %>
|
324
|
-
```
|
325
|
-
|
326
|
-
`app/views/no_header.html.erb`:
|
327
|
-
```erb
|
328
|
-
<%= render(ModalComponent.new) do |component| %>
|
329
|
-
<% component.with(:body) do %>
|
330
|
-
<p>Have a great day.</p>
|
331
|
-
<% end %>
|
332
|
-
<% end %>
|
333
|
-
```
|
334
|
-
|
335
200
|
### Conditional Rendering
|
336
201
|
|
337
|
-
Components can implement a `#render?` method
|
202
|
+
Components can implement a `#render?` method to determine if they should be rendered.
|
338
203
|
|
339
|
-
For example,
|
204
|
+
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:
|
340
205
|
|
206
|
+
`app/components/confirm_email_component.html.erb`
|
341
207
|
```
|
342
|
-
<!-- app/components/confirm_email_component.html.erb -->
|
343
208
|
<% if user.requires_confirmation? %>
|
344
209
|
<div class="alert">
|
345
210
|
Please confirm your email address.
|
@@ -349,17 +214,17 @@ For example, you might have a component that displays a "Please confirm your ema
|
|
349
214
|
|
350
215
|
or the view that renders the component:
|
351
216
|
|
217
|
+
`app/views/_banners.html.erb`
|
352
218
|
```erb
|
353
|
-
<!-- app/views/_banners.html.erb -->
|
354
219
|
<% if current_user.requires_confirmation? %>
|
355
220
|
<%= render(ConfirmEmailComponent.new(user: current_user)) %>
|
356
221
|
<% end %>
|
357
222
|
```
|
358
223
|
|
359
|
-
|
224
|
+
Instead, the `#render?` hook expresses this logic in the Ruby class, simplifying the view:
|
360
225
|
|
226
|
+
`app/components/confirm_email_component.rb`
|
361
227
|
```ruby
|
362
|
-
# app/components/confirm_email_component.rb
|
363
228
|
class ConfirmEmailComponent < ViewComponent::Base
|
364
229
|
def initialize(user:)
|
365
230
|
@user = user
|
@@ -368,26 +233,24 @@ class ConfirmEmailComponent < ViewComponent::Base
|
|
368
233
|
def render?
|
369
234
|
@user.requires_confirmation?
|
370
235
|
end
|
371
|
-
|
372
|
-
attr_reader :user
|
373
236
|
end
|
374
237
|
```
|
375
238
|
|
239
|
+
`app/components/confirm_email_component.html.erb`
|
376
240
|
```
|
377
|
-
<!-- app/components/confirm_email_component.html.erb -->
|
378
241
|
<div class="banner">
|
379
242
|
Please confirm your email address.
|
380
243
|
</div>
|
381
244
|
```
|
382
245
|
|
246
|
+
`app/views/_banners.html.erb`
|
383
247
|
```erb
|
384
|
-
<!-- app/views/_banners.html.erb -->
|
385
248
|
<%= render(ConfirmEmailComponent.new(user: current_user)) %>
|
386
249
|
```
|
387
250
|
|
388
251
|
### Testing
|
389
252
|
|
390
|
-
|
253
|
+
Unit test components directly, using the `render_inline` test helper and Capybara matchers:
|
391
254
|
|
392
255
|
```ruby
|
393
256
|
require "view_component/test_case"
|
@@ -401,11 +264,9 @@ class MyComponentTest < ViewComponent::TestCase
|
|
401
264
|
end
|
402
265
|
```
|
403
266
|
|
404
|
-
In general, we’ve found it makes the most sense to test components based on their rendered HTML.
|
405
|
-
|
406
267
|
#### Action Pack Variants
|
407
268
|
|
408
|
-
|
269
|
+
Use the `with_variant` helper to test specific variants:
|
409
270
|
|
410
271
|
```ruby
|
411
272
|
test "render component for tablet" do
|
@@ -418,14 +279,10 @@ end
|
|
418
279
|
```
|
419
280
|
|
420
281
|
### Previewing Components
|
421
|
-
`ViewComponent::Preview` provides a way to
|
422
|
-
In the previous example, the preview class for `TestComponent` would be called `TestComponentPreview` and located in `test/components/previews/test_component_preview.rb`.
|
423
|
-
To see the preview of the component with a given title, implement a method that renders the component.
|
424
|
-
You can define as many examples as you want:
|
282
|
+
`ViewComponent::Preview`, like `ActionMailer::Preview`, provides a way to preview components in isolation:
|
425
283
|
|
284
|
+
`test/components/previews/test_component_preview.rb`
|
426
285
|
```ruby
|
427
|
-
# test/components/previews/test_component_preview.rb
|
428
|
-
|
429
286
|
class TestComponentPreview < ViewComponent::Preview
|
430
287
|
def with_default_title
|
431
288
|
render(TestComponent.new(title: "Test component default"))
|
@@ -434,17 +291,33 @@ class TestComponentPreview < ViewComponent::Preview
|
|
434
291
|
def with_long_title
|
435
292
|
render(TestComponent.new(title: "This is a really long title to see how the component renders this"))
|
436
293
|
end
|
294
|
+
|
295
|
+
def with_content_block
|
296
|
+
render(TestComponent.new(title: "This component accepts a block of content") do
|
297
|
+
tag.div do
|
298
|
+
content_tag(:span, "Hello")
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
437
302
|
end
|
438
303
|
```
|
439
304
|
|
440
|
-
|
441
|
-
|
305
|
+
Which generates <http://localhost:3000/rails/components/test_component/with_default_title>,
|
306
|
+
<http://localhost:3000/rails/components/test_component/with_long_title>,
|
307
|
+
and <http://localhost:3000/rails/components/test_component/with_content_block>.
|
442
308
|
|
443
|
-
|
309
|
+
The `ViewComponent::Preview` base class includes
|
310
|
+
[`ActionView::Helpers::TagHelper`][tag-helper], which provides the [`tag`][tag]
|
311
|
+
and [`content_tag`][content_tag] view helper methods.
|
444
312
|
|
445
|
-
|
446
|
-
|
313
|
+
[tag-helper]: https://api.rubyonrails.org/classes/ActionView/Helpers/TagHelper.html
|
314
|
+
[tag]: https://api.rubyonrails.org/classes/ActionView/Helpers/TagHelper.html#method-i-tag
|
315
|
+
[content_tag]: https://api.rubyonrails.org/classes/ActionView/Helpers/TagHelper.html#method-i-content_tag
|
447
316
|
|
317
|
+
Previews default to the application layout, but can be overridden:
|
318
|
+
|
319
|
+
`test/components/previews/test_component_preview.rb`
|
320
|
+
```ruby
|
448
321
|
class TestComponentPreview < ViewComponent::Preview
|
449
322
|
layout "admin"
|
450
323
|
|
@@ -452,52 +325,46 @@ class TestComponentPreview < ViewComponent::Preview
|
|
452
325
|
end
|
453
326
|
```
|
454
327
|
|
455
|
-
|
456
|
-
This can be configured using the `preview_path` option.
|
457
|
-
For example, if you want to use `lib/component_previews`, set the following in `config/application.rb`:
|
328
|
+
Preview classes live in `test/components/previews`, can be configured using the `preview_path` option.
|
458
329
|
|
330
|
+
To use `lib/component_previews`:
|
331
|
+
|
332
|
+
`config/application.rb`
|
459
333
|
```ruby
|
460
334
|
config.action_view_component.preview_path = "#{Rails.root}/lib/component_previews"
|
461
335
|
```
|
462
336
|
|
463
337
|
#### Configuring TestController
|
464
338
|
|
465
|
-
|
466
|
-
This can be configured using the `test_controller` option.
|
467
|
-
For example, if your controllers inherit from `BaseController`, set the following in `config/application.rb`:
|
339
|
+
Component tests and previews assume the existence of an `ApplicationController` class, be can beconfigured using the `test_controller` option:
|
468
340
|
|
341
|
+
`config/application.rb`
|
469
342
|
```ruby
|
470
343
|
config.action_view_component.test_controller = "BaseController"
|
471
344
|
```
|
472
345
|
|
473
346
|
### Setting up RSpec
|
474
347
|
|
475
|
-
|
476
|
-
`spec/rails_helper.rb`:
|
348
|
+
To use RSpec, add the following:
|
477
349
|
|
350
|
+
`spec/rails_helper.rb`
|
478
351
|
```ruby
|
479
352
|
require "view_component/test_helpers"
|
480
353
|
|
481
354
|
RSpec.configure do |config|
|
482
|
-
|
483
|
-
|
484
|
-
# Ensure that the test helpers are available in component specs
|
485
|
-
config.include ViewComponent::TestHelpers, type: :component
|
355
|
+
config.include ViewComponent::TestHelpers, type: :component
|
486
356
|
end
|
487
357
|
```
|
488
358
|
|
489
|
-
Specs created by the generator
|
359
|
+
Specs created by the generator have access to test helpers like `render_inline`.
|
490
360
|
|
491
|
-
To use component previews
|
361
|
+
To use component previews:
|
492
362
|
|
363
|
+
`config/application.rb`
|
493
364
|
```ruby
|
494
365
|
config.action_view_component.preview_path = "#{Rails.root}/spec/components/previews"
|
495
366
|
```
|
496
367
|
|
497
|
-
### Initializer requirement
|
498
|
-
|
499
|
-
`ViewComponent` requires the presence of an `initialize` method in each component.
|
500
|
-
|
501
368
|
## Frequently Asked Questions
|
502
369
|
|
503
370
|
### Can I use other templating languages besides ERB?
|
@@ -523,11 +390,11 @@ Inline templates have been removed (for now) due to concerns raised by [@soutaro
|
|
523
390
|
- [Introducing ActionView::Component with Joel Hawksley, Ruby on Rails Podcast](http://5by5.tv/rubyonrails/276)
|
524
391
|
- [Rails to Introduce View Components, Dev.to](https://dev.to/andy/rails-to-introduce-view-components-3ome)
|
525
392
|
- [ActionView::Components in Rails 6.1, Drifting Ruby](https://www.driftingruby.com/episodes/actionview-components-in-rails-6-1)
|
526
|
-
- [Demo repository,
|
393
|
+
- [Demo repository, view-component-demo](https://github.com/joelhawksley/view-component-demo)
|
527
394
|
|
528
395
|
## Contributing
|
529
396
|
|
530
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/github/
|
397
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/github/view_component. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct. We recommend reading the [contributing guide](./CONTRIBUTING.md) as well.
|
531
398
|
|
532
399
|
## Contributors
|
533
400
|
|
@@ -563,6 +430,11 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/github
|
|
563
430
|
|@blakewilliams|
|
564
431
|
|Boston, MA|
|
565
432
|
|
433
|
+
|<img src="https://avatars.githubusercontent.com/seanpdoyle?s=256" alt="seanpdoyle" width="128" />|
|
434
|
+
|:---:|
|
435
|
+
|@seanpdoyle|
|
436
|
+
|New York, NY|
|
437
|
+
|
566
438
|
## License
|
567
439
|
|
568
440
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
@@ -9,11 +9,11 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.name = "actionview-component"
|
10
10
|
spec.version = ViewComponent::VERSION::STRING
|
11
11
|
spec.authors = ["GitHub Open Source"]
|
12
|
-
spec.email = ["opensource+
|
12
|
+
spec.email = ["opensource+view_component@github.com"]
|
13
13
|
|
14
14
|
spec.summary = %q{View components for Rails}
|
15
|
-
spec.description = %q{View components for Rails
|
16
|
-
spec.homepage = "https://github.com/github/
|
15
|
+
spec.description = %q{View components for Rails}
|
16
|
+
spec.homepage = "https://github.com/github/view_component"
|
17
17
|
spec.license = "MIT"
|
18
18
|
|
19
19
|
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
@@ -0,0 +1,76 @@
|
|
1
|
+
Name: Dave Paola
|
2
|
+
|
3
|
+
Github Handle: [@dpaola2](https://github.com/dpaola2)
|
4
|
+
|
5
|
+
[Jellyswitch](https://www.jellyswitch.com) is a coworking space management platform
|
6
|
+
|
7
|
+
In response to [this tweet](https://twitter.com/joelhawksley/status/1232674647327547394):
|
8
|
+
|
9
|
+
I recently began refactoring many of my partials into components. Along the way I discovered an interesting use case, which was to componentize the various bootstrap components I was already using.
|
10
|
+
|
11
|
+
Some examples:
|
12
|
+
|
13
|
+
- I've componentized the specific layout that I've designed using the grid system. I have defined a `FullWidthLayout` component that wraps its contents in the correct classes to give my layout a good design on both mobile and desktop. (On desktop, there is a sidebar, and on mobile, the sidebar floats on top in a collapsed fashion.)
|
14
|
+
- [Modals](https://getbootstrap.com/docs/4.4/components/modal/) are now componentized and accept arguments. I had them as partials before, but having ruby classes is an upgrade.
|
15
|
+
- [Breadcrumbs](https://getbootstrap.com/docs/4.4/components/breadcrumb/) same as above.
|
16
|
+
|
17
|
+
Here's one that I use a LOT: `OnOffSwitch`:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
class OnOffSwitch < ApplicationComponent
|
21
|
+
def initialize(predicate:, path:, disabled: false, label: nil)
|
22
|
+
@predicate = predicate
|
23
|
+
@path = path
|
24
|
+
@disabled = disabled
|
25
|
+
@label = label
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
attr_reader :predicate, :path, :disabled, :label
|
31
|
+
|
32
|
+
def icon_class
|
33
|
+
if predicate
|
34
|
+
"fas fa-toggle-on"
|
35
|
+
else
|
36
|
+
"fas fa-toggle-off"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
```
|
41
|
+
|
42
|
+
```erb
|
43
|
+
<div class="d-flex align-items-center">
|
44
|
+
<% if !disabled %>
|
45
|
+
<%= link_to path, class: "text-body", remote: true do %>
|
46
|
+
<span style="font-size: 20pt">
|
47
|
+
<% if predicate %>
|
48
|
+
<span class="text-success">
|
49
|
+
<i class="<%= icon_class %>"></i>
|
50
|
+
</span>
|
51
|
+
<% else %>
|
52
|
+
<span class="text-danger">
|
53
|
+
<i class="<%= icon_class %>"></i>
|
54
|
+
</span>
|
55
|
+
<% end %>
|
56
|
+
</span>
|
57
|
+
<% end %>
|
58
|
+
<% else %>
|
59
|
+
<span style="font-size: 20pt">
|
60
|
+
<span class="text-muted">
|
61
|
+
<i class="<%= icon_class %>"></i>
|
62
|
+
</span>
|
63
|
+
</span>
|
64
|
+
<% end %>
|
65
|
+
|
66
|
+
<%= content %>
|
67
|
+
</div>
|
68
|
+
```
|
69
|
+
|
70
|
+
Here is an example of how this looks:
|
71
|
+
|
72
|
+
<img width="653" alt="Screenshot 2020-02-26 08 34 07" src="https://user-images.githubusercontent.com/150509/75365920-cbfb9500-5872-11ea-8234-f1343629a462.png">
|
73
|
+
|
74
|
+
I have found that refactoring complex views is made easier and faster by first putting them into a component, extracting the conditionals and other logic into private methods and proceeding from there. And I wind up with a very nice set of well-factored components and sub-components, with argument lists and validations and so on. I think the rails community is really going to benefit from this library, and I'm hugely appreciative of y'all's efforts on it!
|
75
|
+
|
76
|
+
I plan to release an early version of the bootstrap components we've developed at some point in the near future. I would love to collaborate and learn the most appropriate way to structure that contribution.
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionView
|
4
|
+
module Component
|
5
|
+
module TestHelpers
|
6
|
+
include ViewComponent::TestHelpers
|
7
|
+
|
8
|
+
def render_component(component, **args, &block)
|
9
|
+
ActiveSupport::Deprecation.warn(
|
10
|
+
"`render_component` has been deprecated in favor of `render_inline`, and will be removed in v2.0.0."
|
11
|
+
)
|
12
|
+
|
13
|
+
render_inline(component, args, &block)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/view_component.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
module ViewComponent
|
4
4
|
module RenderMonkeyPatch # :nodoc:
|
5
5
|
def render(options = {}, args = {}, &block)
|
6
|
-
if options.respond_to?(:render_in)
|
6
|
+
if options.respond_to?(:render_in) && Rails.version.to_f < 6.1
|
7
7
|
options.render_in(self, &block)
|
8
8
|
elsif options.is_a?(Class) && options < ActionView::Component::Base
|
9
9
|
ActiveSupport::Deprecation.warn(
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module ViewComponent
|
4
4
|
module RenderingMonkeyPatch # :nodoc:
|
5
5
|
def render(options = {}, args = {})
|
6
|
-
if options.respond_to?(:render_in)
|
6
|
+
if options.respond_to?(:render_in) && Rails.version.to_f < 6.1
|
7
7
|
self.response_body = options.render_in(self.view_context)
|
8
8
|
else
|
9
9
|
super
|
@@ -24,14 +24,6 @@ module ViewComponent
|
|
24
24
|
@request ||= ActionDispatch::TestRequest.create
|
25
25
|
end
|
26
26
|
|
27
|
-
def render_component(component, **args, &block)
|
28
|
-
ActiveSupport::Deprecation.warn(
|
29
|
-
"`render_component` has been deprecated in favor of `render_inline`, and will be removed in v2.0.0."
|
30
|
-
)
|
31
|
-
|
32
|
-
render_inline(component, args, &block)
|
33
|
-
end
|
34
|
-
|
35
27
|
def with_variant(variant)
|
36
28
|
old_variants = controller.view_context.lookup_context.variants
|
37
29
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: actionview-component
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.15.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GitHub Open Source
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-03-
|
11
|
+
date: 2020-03-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: capybara
|
@@ -136,9 +136,9 @@ dependencies:
|
|
136
136
|
- - "~>"
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: 0.13.0
|
139
|
-
description: View components for Rails
|
139
|
+
description: View components for Rails
|
140
140
|
email:
|
141
|
-
- opensource+
|
141
|
+
- opensource+view_component@github.com
|
142
142
|
executables: []
|
143
143
|
extensions: []
|
144
144
|
extra_rdoc_files: []
|
@@ -158,11 +158,13 @@ files:
|
|
158
158
|
- Rakefile
|
159
159
|
- actionview-component.gemspec
|
160
160
|
- app/controllers/rails/components_controller.rb
|
161
|
+
- docs/case-studies/jellyswitch.md
|
161
162
|
- lib/action_view/component.rb
|
162
163
|
- lib/action_view/component/base.rb
|
163
164
|
- lib/action_view/component/preview.rb
|
164
165
|
- lib/action_view/component/railtie.rb
|
165
166
|
- lib/action_view/component/test_case.rb
|
167
|
+
- lib/action_view/component/test_helpers.rb
|
166
168
|
- lib/rails/generators/component/USAGE
|
167
169
|
- lib/rails/generators/component/component_generator.rb
|
168
170
|
- lib/rails/generators/component/templates/component.rb.tt
|
@@ -197,7 +199,7 @@ files:
|
|
197
199
|
- script/install
|
198
200
|
- script/release
|
199
201
|
- script/test
|
200
|
-
homepage: https://github.com/github/
|
202
|
+
homepage: https://github.com/github/view_component
|
201
203
|
licenses:
|
202
204
|
- MIT
|
203
205
|
metadata:
|