-
Notifications
You must be signed in to change notification settings - Fork 2
Content Slots
The following situations could warrant a "content slot" approach:
- a space in a component that could include body copy, particularly the possibility of multiple paragraphs, etc. An example of this could be a body area or caption area for a Media Tile component.
- a space that a particular child component but may benefit from other possible children. An example of this could be a "Popover" showing beside a heading in that the particular configuration of the popover may need to be quite flexible, or another element might be desired in the spot such as a Label or a Button.
- an area where a variable set of content may appear. Examples include a button group, toolbar, or grid row.
In these cases it may be preferable to allow for either a property to contain the content or an actual content_for
slot. Here are the steps and files affected including patterns that can be used in such cases.
NOTE: The property name we'll use in these examples is body
and it is inside a fictitious component called "SageFoo" but this can vary based on context. You can also use these patterns multiple times inside of a given component.
-
Add an entry for the property and slot in the component definition file
sage_foo.rb
:class SageFoo < SageComponent set_attribute_schema( body: [:optional, NilClass, String], # other properties as needed... ) def sections %w(foo_body) # more than one slot can be added with a space separating each... end end
-
Account for the possible content slot or property in the component view file
sage_foo.html.erb
:<div class="sage-foo"> <!-- placed in desired position in the view... --> <% if component.body.present? or content_for? :sage_foo_body %> <div class="sage-foo__body"> <%= content_for :sage_foo_body if content_for? :sage_foo_body %> <%= component.body.html_safe if component.body.present? %> </div> <% end %> </div>
Note here that this allows for both the property and the slot to be output. Additional logic could be added if you only want one or the other (not both).
-
(If needed) Build the slot as a property in the React component file
Foo.jsx
(assuming this is not the primary slot, in which casechildren
should be used):export const Foo = ({ body, // other props... }) => { // component code as needed... return ( <div className="sage-foo"> {/* placed in desired position in the view... */} {body && ( <div className="sage-foo__body"> {body} </div> )} </div> ); }; Foo.defaultProps = { body: null, // other prop defaults }; Foo.propTypes = { body: PropTypes.node, // other prop types };
Now the Rails component can use this feature as follows:
<!-- passing the content to the property directly -->
<%= sage_component SageFoo, {
body: %(
<p>Some cool content goes</p>
<a href="//example.com">Learn more...</a>
),
} %>
<!-- using content_for -->
<%= sage_component SageFoo, {} do %>
<%= content_for :sage_foo_body do %>
<p>Some cool content goes</p>
<a href="//example.com">Learn more...</a>
<% end %>
<% end %>
And in React it can be used as follows:
<Foo
body={(
<>
<p>Some cool content goes</p>
<a href="//example.com">Learn more...</a>
</>
)}
/>
Thanks for using Sage!
Welcome
Conventions
Processes and Reference