Skip to content

Commit fc3d532

Browse files
authored
Merge pull request #553 from marcosvafilho/conditional_rendering
Feature: Conditionally render components with a new `render?` method.
2 parents 84eed8b + de38a82 commit fc3d532

File tree

3 files changed

+149
-0
lines changed

3 files changed

+149
-0
lines changed

docs/ui-in-pure-ruby/components/component-api.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,42 @@ class SomeComponent < Matestack::Ui::Component
125125
end
126126
```
127127

128+
## Render?
129+
130+
Use the `render?` method to conditionally render the component based on custom rules:
131+
132+
```ruby
133+
class AdminComponent < Matestack::Ui::Component
134+
required :user
135+
136+
def render?
137+
context.user.admin?
138+
end
139+
140+
def response
141+
div id: "admin-component" do
142+
plain "This component should only get rendered for admins"
143+
end
144+
end
145+
end
146+
```
147+
148+
This is particularly useful to avoid plastering your views with conditional statements like `if` and `unless`.
149+
150+
Instead of:
151+
152+
```ruby
153+
<% if current_user.admin? %>
154+
<%= Components::AdminComponent.(user: current_user) %>
155+
<% end %>
156+
```
157+
158+
You can just use:
159+
160+
```ruby
161+
<%= Components::AdminComponent.(user: current_user) %>
162+
```
163+
128164
## Prepare
129165

130166
Use a prepare method to resolve instance variables before rendering a component if required.

lib/matestack/ui/core/base.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ class Base
1212
attr_accessor :html_tag, :text, :options, :parent, :escape, :bind_to_parent
1313

1414
def initialize(html_tag = nil, text = nil, options = {}, &block)
15+
return unless render?
16+
1517
self.bind_to_parent = ([:without_parent].include?(html_tag) ? false : true)
1618
self.slots = self.options.delete(:slots) if self.options
1719
# extract_options(text, options) is called in properties
@@ -26,6 +28,12 @@ def initialize(html_tag = nil, text = nil, options = {}, &block)
2628
self
2729
end
2830

31+
# can be optionally overwritten in subclass
32+
# in order to conditionally render the component
33+
def render?
34+
true
35+
end
36+
2937
# check if text is given and set text and options accordingly
3038
def extract_options(text, options)
3139
if text.is_a? Hash
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
require_relative "../../../support/utils"
2+
include Utils
3+
4+
describe "Component", type: :feature, js: true do
5+
6+
before :all do
7+
class ComponentTestController < ActionController::Base
8+
include Matestack::Ui::Core::Helper
9+
layout "application"
10+
11+
def my_action
12+
render ExamplePage
13+
end
14+
end
15+
16+
Rails.application.routes.append do
17+
scope "component_conditional_rendering_spec" do
18+
get '/component_test', to: 'component_test#my_action', as: 'conditional_component_test_action'
19+
end
20+
end
21+
Rails.application.reload_routes!
22+
end
23+
24+
describe "conditional component" do
25+
26+
it "renders by default if '.render?' method is not overrided" do
27+
class DefaultRenderingComponent < Matestack::Ui::Component
28+
def response
29+
div id: "my-component" do
30+
plain "default rendering component"
31+
end
32+
end
33+
34+
register_self_as(:default_rendering_component)
35+
end
36+
37+
class ExamplePage < Matestack::Ui::Page
38+
def response
39+
div id: "div-on-page" do
40+
default_rendering_component
41+
end
42+
end
43+
end
44+
45+
visit "component_conditional_rendering_spec/component_test"
46+
expect(page).to have_xpath('//div[@id="div-on-page"]/div[@id="my-component" and contains(.,"default rendering component")]')
47+
end
48+
49+
it "renders if '.render?' method is overrided and returns true" do
50+
class ConditionalRenderingComponent < Matestack::Ui::Component
51+
def response
52+
div id: "my-component" do
53+
plain "conditional rendering component"
54+
end
55+
end
56+
57+
def render?
58+
true
59+
end
60+
61+
register_self_as(:conditional_rendering_component)
62+
end
63+
64+
class ExamplePage < Matestack::Ui::Page
65+
def response
66+
div id: "div-on-page" do
67+
conditional_rendering_component
68+
end
69+
end
70+
end
71+
72+
visit "component_conditional_rendering_spec/component_test"
73+
expect(page).to have_xpath('//div[@id="div-on-page"]/div[@id="my-component" and contains(.,"conditional rendering component")]')
74+
end
75+
76+
it "doesn't render if '.render?' method is overrided and returns false" do
77+
class ConditionalRenderingComponent < Matestack::Ui::Component
78+
def response
79+
div id: "my-component" do
80+
plain "conditional rendering component"
81+
end
82+
end
83+
84+
def render?
85+
false
86+
end
87+
88+
register_self_as(:conditional_rendering_component)
89+
end
90+
91+
class ExamplePage < Matestack::Ui::Page
92+
def response
93+
div id: "div-on-page" do
94+
conditional_rendering_component
95+
end
96+
end
97+
end
98+
99+
visit "component_conditional_rendering_spec/component_test"
100+
expect(page).to_not have_xpath('//div[@id="div-on-page"]/div[@id="my-component" and contains(.,"conditional rendering component")]')
101+
end
102+
103+
end
104+
105+
end

0 commit comments

Comments
 (0)