Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upstream ApplicationComponent niceties from app to Rbexy #106

Open
patbenatar opened this issue Feb 26, 2024 · 3 comments
Open

Upstream ApplicationComponent niceties from app to Rbexy #106

patbenatar opened this issue Feb 26, 2024 · 3 comments
Labels
enhancement New feature or request

Comments

@patbenatar
Copy link
Owner

In my main work project, we've developed an ApplicationComponent base class that has a handful of features on top of Rbexy::Component. They're more opinionated and provide more of a framework, so they should perhaps be opt-in but that's up for discussion.

Some of the things we could upstream:

Props

class ThingComponent < Rbexy::Component
  prop :something, default: "value1", options: ["value1", "value2"]
end

Which can then be called as something in the template or component class.

And are used by the caller like so:

<Thing something="value1" />

Root props

By default we delegate any given props that aren't explicitly consumed by the component to the root element of the component's template. This makes it convenient to treat component elements like a superset of HTML elements.

For example:

class HeaderComponent < Rbexy::Component
  prop :title
end

With a template:

<div>
  <h1>{title}</h1>
  {content}
</div>

Used like so:

<Header title="Hello orld" class="my-class">
  Some content here
</Header>

Would result in:

<div class="my-class">
  <h1>Hello World</h1>
  Some content here
</div>

This of course requires that the component template has a single root element just like React requires.

CSS Modules

We use PostCSS in our Webpack/Shakapacker setup to preprocess .scss files in app/components/ and app/views/, generating fully qualified names for each class in those files to keep classes local to components/views and avoid global namespace pollution.

// app/components/my_component.scss
.container {
  background: blue;
}

.headline {
  font-weight: bold;
}

Results in my-component--container and my-component--headline being output to the final asset bundle.

Our rbexy config setup will automatically namespace classes used in class=... attributes to match the compiled CSS, so you can just use them like so:

# app/components/my_component.rbx
<div class="container">
  <h1 class="headline">Hello world</h1>
  <h2 class={true ? "headline" : "not-headline"}>Welcome</h2>
</div>

This naming convention also applies to Stimulus controllers.

Hotwire integration

We have some niceties for wiring up a component with a Stimulus controller:

<div {**sjs_controller(values: {...})}>
  <button data-action={sjs_name("#onClick")}>Click me</button>
  <div {**sjs_target("content")}>Content container<div>
</div>

And some base components for working with Turbo frames and streams.

Atomic Design

We implement a framework for organizing components inspired by Atomic Design:

app/components/
  atoms/
  molecules/
  organisms/

With component classes, templates, CSS, and JS colocated:

app/components/atoms/
  button_component.rb
  button_component.rbx
  button_component.scss
  button_controller.js
@patbenatar patbenatar added the enhancement New feature or request label Feb 26, 2024
@patbenatar
Copy link
Owner Author

@zacheryph curious to get your thoughts on the above and what (if any) might make sense to upstream into this project

@zacheryph
Copy link
Contributor

@patbenatar woops, I missed the notification for this.

root props

I like it. naming be damned, but I was actually thinking about an attribute method to... do this exact thing. (My idea was to expand this to allow {attributes :prop, :names} at the top of a .rbx file to easily add attributes without having to implement the component ruby class.

CSS modules

This definitely seems like something that you'd want people to be able to enable.

Hotwire Integration

I am only lately getting to learn Hotwire, the way stimulus handles data- tags kind of drives me a little nuts (i find the inconsistency between data-[...] vs data-[identifier]-[...] to be a little crazy but meh... This I feel probably needs a few iterations before settling on sjs_ methods? I did think of having some core [rails] components that might be friendly. Ala <Turbo.Frame ...> granted this doesn't help the stimulus case at all.

@zacheryph
Copy link
Contributor

@patbenatar

In regards to your atomic design, I was going to open a ticket to ask what you think about allowing web component'esque referencing to components. an alternative to the namespace lookup could be, using your atomic example, allow tags like <molecule-form-input> and looking the component up based on that. This does get a little hairy in that it would probably want to require components be within a namespace, and how do you easily resolve the above to say Molecule::FormInput vs Molecule::Form::Input?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants