amber_component 0.0.4 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CONTRIBUTING.md +2 -2
- data/Gemfile.lock +9 -9
- data/README.md +424 -4
- data/banner.png +0 -0
- data/lib/amber_component/minitest_test_case.rb +10 -0
- data/lib/amber_component/props.rb +1 -1
- data/lib/amber_component/test_helper.rb +34 -0
- data/lib/amber_component/version.rb +1 -1
- data/lib/generators/amber_component/install_generator.rb +20 -1
- data/lib/generators/amber_component/templates/application_component_test_case.rb +7 -0
- data/lib/generators/amber_component_generator.rb +56 -4
- data/lib/generators/component_generator.rb +9 -0
- data/lib/generators/templates/component.rb.erb +3 -1
- data/lib/generators/templates/component_test.rb.erb +11 -3
- data/lib/generators/templates/style.css.erb +1 -1
- data/lib/generators/templates/style.sass.erb +3 -0
- data/lib/generators/templates/style.scss.erb +5 -0
- data/lib/generators/templates/view.haml.erb +9 -0
- data/lib/generators/templates/view.html.erb.erb +8 -0
- data/lib/generators/templates/view.slim.erb +6 -0
- metadata +11 -31
- data/docs/.bundle/config +0 -2
- data/docs/.gitignore +0 -5
- data/docs/404.html +0 -25
- data/docs/Gemfile +0 -37
- data/docs/Gemfile.lock +0 -89
- data/docs/README.md +0 -19
- data/docs/_config.yml +0 -148
- data/docs/_data/amber_component.yml +0 -3
- data/docs/_sass/_variables.scss +0 -2
- data/docs/_sass/color_schemes/amber_component.scss +0 -11
- data/docs/_sass/custom/custom.scss +0 -60
- data/docs/api/index.md +0 -8
- data/docs/assets/images/logo_wide.png +0 -0
- data/docs/changelog/index.md +0 -8
- data/docs/favicon.ico +0 -0
- data/docs/getting_started/index.md +0 -8
- data/docs/getting_started/installation.md +0 -7
- data/docs/getting_started/ruby_support.md +0 -7
- data/docs/getting_started/wireframes.md +0 -7
- data/docs/index.md +0 -17
- data/docs/introduction/basic_usage.md +0 -7
- data/docs/introduction/index.md +0 -8
- data/docs/introduction/why_amber_component.md +0 -7
- data/docs/resources/index.md +0 -8
- data/docs/styles/index.md +0 -8
- data/docs/styles/usage.md +0 -7
- data/docs/views/index.md +0 -8
- data/docs/views/usage.md +0 -7
- data/lib/generators/templates/view.html.erb +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d81663eb6f3cd153cb0a277a0114f8cce4e2140f40685f23a688a60c8ce86226
|
4
|
+
data.tar.gz: 32abb3383e052466435f318e1f8c4010adfa5f3d7e93e588e7e2842ff7cd59e6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4a3df277a91914b6a148cc72c1b3981f78c420e773b408c8555a8915424d11667c804f113c2a1fd76bce751083607086ab660f1634855fcec686bc433b875c12
|
7
|
+
data.tar.gz: 330efd82e1589a4804f72f4dcb1df1f9a9b90696ce2dfb542c69b91dd1b0a5b7797736c9fa8ce461f73ff7bb16a3966a8ed00b8340c6d047d289acde976240cc
|
data/CONTRIBUTING.md
CHANGED
@@ -72,9 +72,9 @@ $ gem install ffi -- --with-cflags="-fdeclspec"
|
|
72
72
|
|
73
73
|
> Gem::Ext::BuildError: ERROR: Failed to build gem native extension.
|
74
74
|
>
|
75
|
-
> current directory:
|
75
|
+
> current directory: ~/.rvm/gems/ruby-3.1.0@dupa/gems/puma-5.6.2/ext/puma_http11
|
76
76
|
>
|
77
|
-
>
|
77
|
+
> ~/.rvm/rubies/ruby-3.1.0/bin/ruby -I ~/.rvm/rubies/ruby-3.1.0/lib/ruby/3.1.0 -r ./siteconf20220219-40641-4uxhq6.rb extconf.rb --with-cflags\=-Wno-error\=implicit-function-declaration
|
78
78
|
>
|
79
79
|
> checking for BIO_read() in -lcrypto... *** extconf.rb failed ***
|
80
80
|
>
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
amber_component (0.0
|
4
|
+
amber_component (1.0.0)
|
5
5
|
actionview (>= 6)
|
6
6
|
activemodel (>= 6)
|
7
7
|
activesupport (>= 6)
|
@@ -67,10 +67,10 @@ GEM
|
|
67
67
|
method_source (1.0.0)
|
68
68
|
mini_portile2 (2.8.0)
|
69
69
|
minitest (5.16.2)
|
70
|
-
nokogiri (1.13.
|
70
|
+
nokogiri (1.13.9)
|
71
71
|
mini_portile2 (~> 2.8.0)
|
72
72
|
racc (~> 1.4)
|
73
|
-
nokogiri (1.13.
|
73
|
+
nokogiri (1.13.9-arm64-darwin)
|
74
74
|
racc (~> 1.4)
|
75
75
|
parallel (1.22.1)
|
76
76
|
parser (3.1.2.1)
|
@@ -94,21 +94,21 @@ GEM
|
|
94
94
|
rainbow (3.1.1)
|
95
95
|
rake (13.0.6)
|
96
96
|
rchardet (1.8.0)
|
97
|
-
regexp_parser (2.
|
97
|
+
regexp_parser (2.6.0)
|
98
98
|
reverse_markdown (2.1.1)
|
99
99
|
nokogiri
|
100
100
|
rexml (3.2.5)
|
101
|
-
rubocop (1.
|
101
|
+
rubocop (1.38.0)
|
102
102
|
json (~> 2.3)
|
103
103
|
parallel (~> 1.10)
|
104
104
|
parser (>= 3.1.2.1)
|
105
105
|
rainbow (>= 2.2.2, < 4.0)
|
106
106
|
regexp_parser (>= 1.8, < 3.0)
|
107
107
|
rexml (>= 3.2.5, < 4.0)
|
108
|
-
rubocop-ast (>= 1.
|
108
|
+
rubocop-ast (>= 1.23.0, < 2.0)
|
109
109
|
ruby-progressbar (~> 1.7)
|
110
110
|
unicode-display_width (>= 1.4.0, < 3.0)
|
111
|
-
rubocop-ast (1.
|
111
|
+
rubocop-ast (1.23.0)
|
112
112
|
parser (>= 3.1.1.0)
|
113
113
|
ruby-progressbar (1.11.0)
|
114
114
|
ruby2_keywords (0.0.5)
|
@@ -124,7 +124,7 @@ GEM
|
|
124
124
|
simplecov (~> 0.19)
|
125
125
|
simplecov-html (0.12.3)
|
126
126
|
simplecov_json_formatter (0.1.4)
|
127
|
-
solargraph (0.
|
127
|
+
solargraph (0.47.2)
|
128
128
|
backport (~> 1.2)
|
129
129
|
benchmark
|
130
130
|
bundler (>= 1.17.2)
|
@@ -172,4 +172,4 @@ DEPENDENCIES
|
|
172
172
|
yard
|
173
173
|
|
174
174
|
BUNDLED WITH
|
175
|
-
2.3.
|
175
|
+
2.3.7
|
data/README.md
CHANGED
@@ -9,13 +9,13 @@
|
|
9
9
|
|
10
10
|
# AmberComponent
|
11
11
|
|
12
|
-
AmberComponent is a simple component library which seamlessly hooks into your Rails project and allows you to create simple backend components. They work like mini controllers which are bound with their view.
|
12
|
+
AmberComponent is a simple component library which seamlessly hooks into your Rails project and allows you to create simple backend components. They work like mini controllers which are bound with their view and stylesheet.
|
13
13
|
|
14
14
|
Created by [Garbus Beach](https://github.com/garbusbeach) and [Mateusz Drewniak](https://github.com/Verseth).
|
15
15
|
|
16
16
|
## Getting started
|
17
17
|
|
18
|
-
You can read a lot more about AmberComponent in its [official docs](https://ambercomponent.com).
|
18
|
+
You can read a lot more about AmberComponent in its [official docs](https://ambercomponent.com).
|
19
19
|
|
20
20
|
## Installation
|
21
21
|
|
@@ -27,14 +27,434 @@ If bundler is not being used to manage dependencies, install the gem by executin
|
|
27
27
|
|
28
28
|
$ gem install amber_component
|
29
29
|
|
30
|
+
If you're using a Rails application there's an installation generator that you should run:
|
31
|
+
|
32
|
+
```sh
|
33
|
+
$ bin/rails generate amber_component:install
|
34
|
+
```
|
35
|
+
|
30
36
|
## Usage
|
31
37
|
|
32
|
-
|
38
|
+
## Components
|
39
|
+
|
40
|
+
Components are located under `app/components`.
|
41
|
+
|
42
|
+
Every component consists of:
|
43
|
+
- a Ruby file which defines its properties, encapsulates logic and may implement helper methods (like a controller)
|
44
|
+
- a view/template file (html.erb, haml, slim etc.)
|
45
|
+
- a style file (css, scss, sass etc.)
|
46
|
+
|
47
|
+
An individual component which implements a button may look like this.
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
# app/components/button_component.rb
|
51
|
+
|
52
|
+
class ButtonComponent < AmberComponent::Base
|
53
|
+
prop :label, required: true
|
54
|
+
end
|
55
|
+
```
|
56
|
+
|
57
|
+
```html
|
58
|
+
<!-- app/components/button_component/view.html.erb -->
|
59
|
+
|
60
|
+
<div class="button_component">
|
61
|
+
<%= label %>
|
62
|
+
</div>
|
63
|
+
```
|
64
|
+
|
65
|
+
```scss
|
66
|
+
// app/components/button_component/style.scss
|
67
|
+
|
68
|
+
.button_component {
|
69
|
+
background-color: indigo;
|
70
|
+
border-radius: 1rem;
|
71
|
+
transition-duration: 500ms;
|
72
|
+
|
73
|
+
&:hover {
|
74
|
+
background-color: blue;
|
75
|
+
}
|
76
|
+
}
|
77
|
+
```
|
78
|
+
|
79
|
+
You can render this component in other components or in a Rails view.
|
80
|
+
|
81
|
+
```html
|
82
|
+
<!-- app/controller/foo/index.html.erb -->
|
83
|
+
|
84
|
+
<h1>We're inside FooController</h1>
|
85
|
+
|
86
|
+
<!-- using a helper method -->
|
87
|
+
<%= button_component label: 'Click me!' %>
|
88
|
+
<!-- calling a method on the component class -->
|
89
|
+
<%= ButtonComponent.call label: 'Click me!' %>
|
90
|
+
```
|
91
|
+
|
92
|
+
Or even directly in Ruby
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
# Calling a method on the component class. Outputs an HTML string.
|
96
|
+
ButtonComponent.call label: 'Click me!'
|
97
|
+
#=> '<div class="button_component">Click me!</div>'
|
98
|
+
```
|
99
|
+
|
100
|
+
### Rails helpers inside component templates
|
101
|
+
|
102
|
+
Component views/template files can make use
|
103
|
+
of all ActionView helpers and Rails route helpers.
|
104
|
+
|
105
|
+
This makes component views very flexible and convenient.
|
106
|
+
|
107
|
+
```erb
|
108
|
+
<!-- app/components/login_form_component/view.html.erb -->
|
109
|
+
|
110
|
+
<%= form_with url: sign_up_path, class: "login_form_component" do |f| %>
|
111
|
+
<%= f.label :first_name %>
|
112
|
+
<%= f.text_field :first_name %>
|
113
|
+
|
114
|
+
<%= f.label :last_name %>
|
115
|
+
<%= f.text_field :last_name %>
|
116
|
+
|
117
|
+
<%= f.label :email, "Email Address" %>
|
118
|
+
<%= f.text_field :email %>
|
119
|
+
|
120
|
+
<%= f.label :password %>
|
121
|
+
<%= f.password_field :password %>
|
122
|
+
|
123
|
+
<%= f.label :password_confirmation, "Confirm Password" %>
|
124
|
+
<%= f.password_field :password_confirmation %>
|
125
|
+
|
126
|
+
<%= f.submit "Create account" %>
|
127
|
+
<% end %>
|
128
|
+
```
|
129
|
+
|
130
|
+
### Component properties
|
131
|
+
|
132
|
+
There is a neat prop DSL.
|
133
|
+
|
134
|
+
```ruby
|
135
|
+
# app/components/comment_component.rb
|
136
|
+
|
137
|
+
class CommentComponent < ApplicationComponent
|
138
|
+
# will raise an error when not present
|
139
|
+
prop :body, required: true
|
140
|
+
# will raise an error when an object of a different
|
141
|
+
# class is received (uses `is_a?`)
|
142
|
+
prop :author, type: User, allow_nil: true
|
143
|
+
# the default value
|
144
|
+
prop :date, default: -> { DateTime.now }
|
145
|
+
end
|
146
|
+
```
|
147
|
+
|
148
|
+
Props can be passed as keyword arguments
|
149
|
+
to the `::call` method of the component class
|
150
|
+
or the helper method.
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
CommentComponent.call body: 'Foo bar', author: User.first
|
154
|
+
# only in views and other components
|
155
|
+
comment_component body: 'Foo bar', author: User.first
|
156
|
+
```
|
157
|
+
|
158
|
+
### Overriding prop getters and setters
|
159
|
+
|
160
|
+
Getters and setters for properties are
|
161
|
+
defined in a module which means that you can override them and call them with `super`.
|
162
|
+
|
163
|
+
```ruby
|
164
|
+
# app/components/priority_icon_component.rb
|
165
|
+
|
166
|
+
class PriorityIconComponent < ApplicationComponent
|
167
|
+
PriorityStruct = Struct.new :icon, :color
|
168
|
+
|
169
|
+
PRIORITY_MAP = {
|
170
|
+
low: PriorityStruct.new('fa-solid fa-chevrons-down', 'green'),
|
171
|
+
medium: PriorityStruct.new('fa-solid fa-chevron-up', 'yellow'),
|
172
|
+
high: PriorityStruct.new('fa-solid fa-chevrons-up', 'red')
|
173
|
+
}
|
174
|
+
|
175
|
+
prop :severity, default: -> { :low }
|
176
|
+
|
177
|
+
def severity=(val)
|
178
|
+
# super will call the original
|
179
|
+
# implementation of the setter
|
180
|
+
super(PRIORITY_MAP[val])
|
181
|
+
end
|
182
|
+
end
|
183
|
+
```
|
184
|
+
|
185
|
+
```html
|
186
|
+
<!-- app/components/priority_icon_component/view.html.erb -->
|
187
|
+
|
188
|
+
<i style="color: <%= severity&.color %>;" class="<%= severity&.icon %>"></i>
|
189
|
+
```
|
190
|
+
|
191
|
+
### Helper methods
|
192
|
+
|
193
|
+
Defining helper methods which are available
|
194
|
+
in the template is extremely easy.
|
195
|
+
|
196
|
+
Just define a method on the component class.
|
197
|
+
|
198
|
+
```ruby
|
199
|
+
# app/components/comment_component.rb
|
200
|
+
|
201
|
+
class CommentComponent < ApplicationComponent
|
202
|
+
# you can also include helper modules
|
203
|
+
include SomeHelper
|
204
|
+
|
205
|
+
prop :body, required: true
|
206
|
+
prop :author, type: Author, allow_nil: true
|
207
|
+
prop :date, default: -> { DateTime.now }
|
208
|
+
|
209
|
+
private
|
210
|
+
|
211
|
+
def humanized_date
|
212
|
+
date.strftime '%Y-%m-%d %H:%M'
|
213
|
+
end
|
214
|
+
|
215
|
+
def author_name
|
216
|
+
author&.name || 'Unknown'
|
217
|
+
end
|
218
|
+
|
219
|
+
def author_avatar
|
220
|
+
author&.avatar_url || User.placeholder_avatar_url
|
221
|
+
end
|
222
|
+
end
|
223
|
+
```
|
224
|
+
|
225
|
+
```html
|
226
|
+
<!-- app/components/comment_component/view.html.erb -->
|
227
|
+
|
228
|
+
<div class="comment_component">
|
229
|
+
<div class="comment_header">
|
230
|
+
<img src="<%= author_avatar %>" alt="<%= author_name %> avatar">
|
231
|
+
|
232
|
+
<div><%= author_name %></div>
|
233
|
+
<div class="comment_date"><%= humanized_date %></div>
|
234
|
+
</div>
|
235
|
+
|
236
|
+
<div class="comment_content">
|
237
|
+
<%= body %>
|
238
|
+
</div>
|
239
|
+
</div>
|
240
|
+
```
|
241
|
+
|
242
|
+
### Nested components
|
243
|
+
|
244
|
+
It's possible to nest components or provide
|
245
|
+
custom HTML to a component.
|
246
|
+
|
247
|
+
This works similarly to React's `props.children`.
|
248
|
+
|
249
|
+
To render the passed nested content call `yield.html_safe` somewhere inside the template/view.
|
250
|
+
|
251
|
+
```ruby
|
252
|
+
# app/components/modal_component.rb
|
253
|
+
|
254
|
+
class ModalComponent < ApplicationComponent
|
255
|
+
prop :id, required: true
|
256
|
+
prop :title, required: true
|
257
|
+
end
|
258
|
+
```
|
259
|
+
|
260
|
+
```html
|
261
|
+
<!-- app/components/modal/view.html.erb -->
|
262
|
+
|
263
|
+
<div id="<%= id %>" class="modal_component">
|
264
|
+
<div class="model_header">
|
265
|
+
<%= title %>
|
266
|
+
</div>
|
267
|
+
|
268
|
+
<div class="modal_body">
|
269
|
+
<!-- nested content will be rendered here -->
|
270
|
+
<%= yield.html_safe %>
|
271
|
+
</div>
|
272
|
+
|
273
|
+
<div class="modal_footer">
|
274
|
+
<div class="modal_close_button"></div>
|
275
|
+
</div>
|
276
|
+
<div>
|
277
|
+
```
|
278
|
+
|
279
|
+
You can pass a body to this modal by passing
|
280
|
+
a block.
|
281
|
+
|
282
|
+
```erb
|
283
|
+
<!-- app/controller/tasks/show.html.erb -->
|
284
|
+
|
285
|
+
<%= ModalComponent.call id: 'update-task-modal' title: 'Update the task' do %>
|
286
|
+
<!-- You can provide HTML and render other components -->
|
287
|
+
<h2>This is your task!</h2>
|
288
|
+
<%= form_with model: @task do |f| %>
|
289
|
+
<%= f.text_field :name %>
|
290
|
+
<%= f.text_area :description %>
|
291
|
+
<%= f.submit %>
|
292
|
+
<% end %>
|
293
|
+
<%= OtherComponent.call some: 'prop' %>
|
294
|
+
<% end %>
|
295
|
+
```
|
296
|
+
|
297
|
+
Note that this will raise an error when no block/nested content is provided.
|
298
|
+
|
299
|
+
In order to render nested content
|
300
|
+
only when it is present (will work without nested content)
|
301
|
+
you can use `yield.html_safe if block_given?`
|
302
|
+
|
303
|
+
In general `block_given?` will return `true` when a block/nested content is present, otherwise `false`.
|
304
|
+
You can use it to render content conditionally based on
|
305
|
+
whether nested content is present.
|
306
|
+
|
307
|
+
### Components with namespaces
|
308
|
+
|
309
|
+
Components may be defined inside multiple modules/namespaces.
|
310
|
+
|
311
|
+
```ruby
|
312
|
+
# app/components/sign_up/button_component.rb
|
313
|
+
|
314
|
+
class SignUp::ButtonComponent < AmberComponent::Base
|
315
|
+
prop :label, required: true
|
316
|
+
end
|
317
|
+
```
|
318
|
+
|
319
|
+
```html
|
320
|
+
<!-- app/components/sign_up/button_component/view.html.erb -->
|
321
|
+
|
322
|
+
<div class="sign_up_button_component">
|
323
|
+
<%= label %>
|
324
|
+
</div>
|
325
|
+
```
|
326
|
+
|
327
|
+
```scss
|
328
|
+
// app/components/sign_up/button_component/style.scss
|
329
|
+
|
330
|
+
.sign_up_button_component {
|
331
|
+
background-color: indigo;
|
332
|
+
border-radius: 1rem;
|
333
|
+
transition-duration: 500ms;
|
334
|
+
|
335
|
+
&:hover {
|
336
|
+
background-color: blue;
|
337
|
+
}
|
338
|
+
}
|
339
|
+
```
|
340
|
+
|
341
|
+
You can render such a component by calling the `::call` method
|
342
|
+
on its class, or by using the helper method defined on its parent module.
|
343
|
+
|
344
|
+
```ruby
|
345
|
+
SignUp::ButtonComponent.call label: 'Sign up!'
|
346
|
+
SignUp.button_component label: 'Sign up!'
|
347
|
+
```
|
348
|
+
|
349
|
+
### Generating Components
|
350
|
+
|
351
|
+
You an generate new components by running
|
33
352
|
|
34
353
|
```sh
|
35
|
-
$ rails generate
|
354
|
+
$ bin/rails generate component foo_bar
|
36
355
|
```
|
37
356
|
|
357
|
+
or
|
358
|
+
|
359
|
+
```sh
|
360
|
+
$ bin/rails generate component FooBar
|
361
|
+
```
|
362
|
+
|
363
|
+
This will generate a new component in `app/components/foo_bar_component.rb` along with a view, stylesheet and test file.
|
364
|
+
|
365
|
+
### Testing Components
|
366
|
+
|
367
|
+
### Rails
|
368
|
+
|
369
|
+
After setting up this gem with the rails generator
|
370
|
+
`rails generate amber_component:install` a new abstract
|
371
|
+
test class will be available called `ApplicationComponentTestCase`.
|
372
|
+
|
373
|
+
It provides a handful of helper methods to make it
|
374
|
+
easier to inspect the rendered HTML.
|
375
|
+
|
376
|
+
A simple test file may look like this:
|
377
|
+
|
378
|
+
```ruby
|
379
|
+
# test/components/foo_component_test.rb
|
380
|
+
|
381
|
+
require 'test_helper'
|
382
|
+
|
383
|
+
class FooComponentTest < ApplicationComponentTestCase
|
384
|
+
test 'render correct HTML' do
|
385
|
+
# Specify what the assertions are supposed to
|
386
|
+
# check against.
|
387
|
+
#
|
388
|
+
# There can be multiple renders in one test
|
389
|
+
# but they override the previous one.
|
390
|
+
# So there is only one rendered component
|
391
|
+
# at any given time.
|
392
|
+
render do
|
393
|
+
FooComponent.call some: 'prop'
|
394
|
+
end
|
395
|
+
|
396
|
+
# Assertions on the rendered HTML
|
397
|
+
|
398
|
+
# Use a CSS selector
|
399
|
+
assert_selector ".foo_component span.my_class", text: 'Some Text'
|
400
|
+
# Check text
|
401
|
+
assert_text 'Amber Component is awesome!'
|
402
|
+
end
|
403
|
+
end
|
404
|
+
```
|
405
|
+
|
406
|
+
A full list of available assertions can be found [here](https://rubydoc.info/github/jnicklas/capybara/Capybara/Node/Matchers).
|
407
|
+
|
408
|
+
### Non Rails
|
409
|
+
|
410
|
+
There is a test case class for minitest. You can
|
411
|
+
access it by requiring `'amber_component/minitest_test_case'`.
|
412
|
+
|
413
|
+
It has the same assertion methods as the Rails test case class.
|
414
|
+
It requires [capybara](https://github.com/teamcapybara/capybara) to be installed and present in the Gemfile.
|
415
|
+
|
416
|
+
A full list of available assertions can be found [here](https://rubydoc.info/github/jnicklas/capybara/Capybara/Node/Matchers).
|
417
|
+
|
418
|
+
```ruby
|
419
|
+
require 'amber_component/minitest_test_case'
|
420
|
+
|
421
|
+
class FooComponentTest < AmberComponent::MinitestTestCase
|
422
|
+
def test_render_correct_html
|
423
|
+
# Specify what the assertions are supposed to
|
424
|
+
# check against.
|
425
|
+
#
|
426
|
+
# There can be multiple renders in one test
|
427
|
+
# but they override the previous one.
|
428
|
+
# So there is only one rendered component
|
429
|
+
# at any given time.
|
430
|
+
render do
|
431
|
+
FooComponent.call some: 'prop'
|
432
|
+
end
|
433
|
+
|
434
|
+
# Assertions on the rendered HTML
|
435
|
+
|
436
|
+
# Use a CSS selector
|
437
|
+
assert_selector ".foo_component span.my_class", text: 'Some Text'
|
438
|
+
# Check text
|
439
|
+
assert_text 'Amber Component is awesome!'
|
440
|
+
end
|
441
|
+
end
|
442
|
+
```
|
443
|
+
|
444
|
+
There is also a helper module which provides all of these assertions
|
445
|
+
under `'amber_component/test_helper'`.
|
446
|
+
|
447
|
+
```ruby
|
448
|
+
require 'amber_component/test_helper'
|
449
|
+
|
450
|
+
class MyAbstractTestCase < ::Minitest::Test
|
451
|
+
include ::AmberComponent::TestHelper
|
452
|
+
end
|
453
|
+
```
|
454
|
+
|
455
|
+
Note that this module has only been tested with minitest and rails test suites,
|
456
|
+
so it may require overriding or implementing a few methods to work with other test suites.
|
457
|
+
|
38
458
|
## Contribute
|
39
459
|
|
40
460
|
Do you want to contribute to AmberComponent? Open the issues page and check for the help wanted label! But before you start coding, please read our [Contributing Guide](https://github.com/amber-ruby/amber_component/blob/main/CONTRIBUTING.md).
|
data/banner.png
CHANGED
Binary file
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AmberComponent
|
4
|
+
# Provides assertions for the rendered
|
5
|
+
# HTML of components.
|
6
|
+
module TestHelper
|
7
|
+
begin
|
8
|
+
require 'capybara/minitest'
|
9
|
+
include ::Capybara::Minitest::Assertions
|
10
|
+
|
11
|
+
def page
|
12
|
+
@page ||= ::Capybara::Node::Simple.new(@rendered_content)
|
13
|
+
end
|
14
|
+
rescue ::LoadError
|
15
|
+
nil
|
16
|
+
end
|
17
|
+
|
18
|
+
# @return [Nokogiri::HTML]
|
19
|
+
def document
|
20
|
+
::Nokogiri::HTML.fragment(@rendered_content)
|
21
|
+
end
|
22
|
+
alias doc document
|
23
|
+
alias html document
|
24
|
+
|
25
|
+
# @param content [String]
|
26
|
+
# @return [Nokogiri::HTML]
|
27
|
+
def render(content = nil)
|
28
|
+
@page = nil
|
29
|
+
@rendered_content = content || yield
|
30
|
+
document
|
31
|
+
end
|
32
|
+
alias render_inline render
|
33
|
+
end
|
34
|
+
end
|
@@ -13,11 +13,30 @@ module ::AmberComponent
|
|
13
13
|
# copy rake tasks
|
14
14
|
def copy_tasks
|
15
15
|
copy_file 'application_component.rb', 'app/components/application_component.rb'
|
16
|
+
copy_file 'application_component_test_case.rb', 'test/application_component_test_case.rb'
|
17
|
+
append_file 'test/test_helper.rb', "require_relative 'application_component_test_case'"
|
16
18
|
|
17
|
-
|
19
|
+
require_components_css_in 'app/assets/stylesheets/application.css'
|
20
|
+
require_components_css_in 'app/assets/stylesheets/application.scss'
|
21
|
+
require_components_css_in 'app/assets/stylesheets/application.sass'
|
22
|
+
require_components_css_in 'app/assets/stylesheets/application.css.scss'
|
23
|
+
require_components_css_in 'app/assets/stylesheets/application.css.sass'
|
24
|
+
require_components_css_in 'app/assets/stylesheets/application.scss.sass'
|
25
|
+
require_components_css_in 'app/assets/stylesheets/application.sass.scss'
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# @param file_name [String]
|
31
|
+
# @return [void]
|
32
|
+
def require_components_css_in(file_name)
|
33
|
+
return unless ::File.exist? file_name
|
34
|
+
|
35
|
+
inject_into_file file_name, after: "*= require_tree .\n" do
|
18
36
|
" *= require_tree ./../../components\n"
|
19
37
|
end
|
20
38
|
end
|
39
|
+
|
21
40
|
end
|
22
41
|
end
|
23
42
|
end
|