Some thoughts on sharing behaviour between components using prepend
#376
-
Hi! We are in the process of setting up a design system using Behaviour shared between all componentsIt'd be cool if all components accept a Specific behaviour shared between some componentsSome components share the same properties with the same values, such as Goal is to mix & match, i.e. some component have a ImplementationTo solve this, I used the following pattern shown below. Thought it might be helpful to share :) First add the properties shared between all components to # app/components/application_component.rb
class ApplicationComponent < ViewComponent::Base
def initialize(html: {})
@html = html
end
end Then extract specific properties into their own modules in This is also the place to provide default arguments and perform validation (via # app/components/concerns/with_size.rb
module WithSize
def initialize(size: :normal, **opts)
raise ArgumentError, "invalid size" unless DesignSystem::Size.sizes.include?(size)
@size = size
super(**opts)
end
end # app/components/concerns/with_color.rb
module WithColor
def initialize(color: :gray, **opts)
raise ArgumentError, "invalid color" unless DesignSystem::Color.colors.include?(color)
@color = color
super(**opts)
end
end # app/components/concerns/with_icon.rb
module WithIcon
def initialize(icon: nil, **opts)
@icon = icon
super(**opts)
end
end Then finally the implementation of the actual components is pretty straight forward: # app/components/label_component.rb
class LabelComponent < ApplicationComponent
prepend WithColor
prepend WithIcon
prepend WithSize
end # app/components/icon_component.rb
class IconComponent < ApplicationComponent
prepend WithSize
def initialize(name:, **opts)
@name = name
super(**opts)
end
def pixel_size
# ... convert @size to pixel size ...
end
end And then to use the components with the various properties that are now available: / large purple label with custom id
= render LabelComponent.new(size: :large, icon: 'at', color: :purple, html: { id: dom_id(...) }) do
| [email protected]
/ icon with html class
= render IconComponent.new(size: :small, name: 'checkmark', html: { class: 'float-right' }) FeedbackWould love to hear from others what you think of this pattern. Or what other patterns you found useful to share behaviour between components! |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 4 replies
-
One thing that came up recently in my use of components which would be made possible by the addition of a root element is for |
Beta Was this translation helpful? Give feedback.
-
@thomasbrus this is looking very similar to something I'm working on at the moment which I posted about previously #341 - I'd see one benefit of the approach I've got as being able to easily constrain what values are allowed for each property - any thoughts/comments are very welcome! |
Beta Was this translation helpful? Give feedback.
-
I remain hesitant about having ViewComponent having any opinion on what it outputs beyond HTML safety. Happy to have my mind changed ❤️ |
Beta Was this translation helpful? Give feedback.
I remain hesitant about having ViewComponent having any opinion on what it outputs beyond HTML safety. Happy to have my mind changed ❤️