Skip to content

Commit

Permalink
First stab at first-component.md
Browse files Browse the repository at this point in the history
  • Loading branch information
willcosgrove committed Sep 3, 2024
1 parent b2bde98 commit 242d341
Showing 1 changed file with 169 additions and 0 deletions.
169 changes: 169 additions & 0 deletions guide/first-component.md
Original file line number Diff line number Diff line change
@@ -1 +1,170 @@
# Building your first component in Phlex

Let's start by building a simple Card component. A Card is a presentational component, it doesn't
really have any behavior. So it's going to be mostly for abstracting away the CSS classes and specific
markup. We'll use simple classes in our example, but you could imagine if you were using a utility
based CSS framework like Tailwind that you would be abstracting away a lot more.

```ruby
class Card < Phlex::HTML
def view_template(&)
div(class: "card", &)
end
end
```

Not the most exciting component, but it's a start. We can use it like this:

::: code-group
```ruby [view.rb]
class View < Phlex::HTML
def view_template
render Card.new do
p { "Card content" }
end
end
end
```
```html [output]
<div class="card">
<p>Card content</p>
</div>
```
:::

This is nice, but some of our cards will need a header section that has a title. Let's keep adding
to our `Card` component so that it can have an optional header:

```ruby
class Card < Phlex::HTML
def view_template(&)
div(class: "card", &)
end
# [!code ++:8]
def header(&)
div(class: "card-header", &)
end

def title(&)
h2(class: "card-title", &)
end
end
```

We've added `header` and `title` methods to our `Card` component. Now when we render a card, we can
use the block argument, which will be the instance of our `Card`, and call our new methods:

::: code-group
```ruby [view.rb]
class View < Phlex::HTML
def view_template
render Card.new do # [!code --]
render Card.new do |c| # [!code ++:4]
c.header do
c.title { "Card Title" }
end
p { "Card content" }
end
end
end
```
```html [output]
<div class="card">
<div class="card-header">
<h2 class="card-title">Card Title</h2>
</div>
<p>Card content</p>
</div>
```
:::

Ok, this is starting to look a bit better. But we also need a way to add an action button to our card.
This will need a bit more flexibility than just changing the content of the button. Let's see what
that looks like:

```ruby
class Card < Phlex::HTML
def view_template(&)
div(class: "card", &)
end

def header(&)
div(class: "card-header", &)
end

def title(&)
h2(class: "card-title", &)
end

def action(**, &) # [!code ++:3]
a(class: "card-action", **, &)
end
end
```

This will let us pass in attributes to the action button.

::: code-group
```ruby [view.rb]
class View < Phlex::HTML
def view_template
render Card.new do |c|
c.header do
c.title { "Card Title" }
end
p { "Card content" }
c.action(href: "/") { "Action" }
end
end
end
```
```html [output]
<div class="card">
<div class="card-header">
<h2 class="card-title">Card Title</h2>
</div>
<p>Card content</p>
<a class="card-action" href="/">Action</a>
</div>
```
:::

This seems like it's working, but we actually have a bit of a problem. We can't specify a class for
our action button without overriding the class provided in the `action` method. Any custom class or
classes we provide will clobber the default `card-action` class. Phlex provides a helper method to help
us deal with this situation called `mix`. It mixes attributes together, and is aware of token lists.
Let's update our `action` method to use `mix`:

```ruby
def action(**attributes, &)
a(**mix(class: "card-action", attributes), &)
end
```

Now if we specify a custom class when we call our `action` method, it will be added to the class list
instead of overriding it.

::: code-group
```ruby [view.rb]
class View < Phlex::HTML
def view_template
render Card.new do |c|
c.header do
c.title { "Card Title" }
c.action(class: "custom-class") { "Action" }
end
p { "Card content" }
end
end
end
```
```html [output]
<div class="card">
<div class="card-header">
<h2 class="card-title">Card Title</h2>
<a class="card-action custom-class" href="/">Action</a>
</div>
<p>Card content</p>
</div>
```
:::

0 comments on commit 242d341

Please sign in to comment.