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

Dynamic boolean attributes #188

Merged
merged 2 commits into from
Nov 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,22 @@

## Main

### Breaking Changes

- Rendering slots is now done by passing the assign with the slot name to the `slot` keyword instead of name as an atom. If this slot has multiple definitions, you can loop through them and render each one individually, or render them all at once. Please see the migration guide for more information.
- The `:default` slot has been renamed to `:inner_block`. This is to be easily compatible with HEEx/Surface. Please see the migration guide for more information.
- Capturing the data being passed into a slot is now defined using the `:let` attribute. Please see the migration guide for more information.

### Enhancements

- Temple components are now compatible with HEEx/Surface components! Some small tweaks to the component implementation has made this possible. Please see the guides for more information.
- Multiple instances of the same slot name can now be declared and then rendered inside the component (similar to HEEx and Surface).
- You can now pass arbitrary data to slots, and it does not need to be a map or a keyword list. I don't think this is a breaking change, but please submit an issue if you notice it is.
- Slot attributes. You can now pass data into a slot from the definition site and use it at the call site (inside the component).

### Breaking Changes
### Fixes

- Rendering slots is now done by passing the assign with the slot name to the `slot` keyword instead of name as an atom. If this slot has multiple definitions, you can loop through them and render each one individually, or render them all at once. Please see the migration guide for more information.
- The `:default` slot has been renamed to `:inner_block`. This is to be easily compatible with HEEx/Surface. Please see the migration guide for more information.
- Capturing the data being passed into a slot is now defined using the `:let` attribute. Please see the migration guide for more information.
- Attributes with runtime values that evaluate to true or false will be rendered correctly as boolean attributes.

### 0.10.0

Expand Down
4 changes: 2 additions & 2 deletions guides/your-first-template.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,8 @@ Attributes are declared as a keyword list.

- Keys with underscores are converted to the kebab syntax.
- Values can be Elixir expressions.
- Values that are compile time `true` will be emitted as a boolean attribute. `disabled` and `checked` are examples of boolean attributes.
- Values that are compile time `false` will not be emitted into the document at all.
- Values that evaluate to `true` will be emitted as a boolean attribute. `disabled` and `checked` are examples of boolean attributes.
- Values that evaluate `false` will not be emitted into the document at all.
- The class attribute has a special "object syntax" that allows you to specify classes as a keyword list, only emitting classes that evaluate to true into the final class.

Let's look at an example.
Expand Down
11 changes: 10 additions & 1 deletion lib/temple/ast/utils.ex
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,16 @@ defmodule Temple.Ast.Utils do
end

def build_attr(name, {_, _, _} = value) do
[{:text, ~s' #{name}="'}, {:expr, value}, {:text, ~s'"'}]
expr =
quote do
case unquote(value) do
true -> " " <> unquote(name)
false -> ""
_ -> ~s' #{unquote(name)}="#{unquote(value)}"'
end
end

[{:expr, expr}]
end

def build_attr("class", classes) when is_list(classes) do
Expand Down
23 changes: 11 additions & 12 deletions test/parser/utils_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,19 @@ defmodule Temple.Ast.UtilsTest do

test "returns a list of text and expr nodes for attributes with runtime values" do
class_ast = quote(do: @class)
id_ast = quote(do: @id)
attrs = [class: class_ast, id: id_ast, disabled: false, checked: true]
attrs = [class: class_ast, id: "foo"]

actual = Utils.compile_attrs(attrs)
assert [{:expr, actual}, {:text, ~s' id="foo"'}] = Utils.compile_attrs(attrs)

assert [
{:text, ~s' class="'},
{:expr, class_ast},
{:text, ~s'"'},
{:text, ~s' id="'},
{:expr, id_ast},
{:text, ~s'"'},
{:text, ~s' checked'}
] == actual
assert Macro.to_string(
quote do
case @class do
true -> " " <> "class"
false -> ""
_ -> ~s' #{"class"}="#{@class}"'
end
end
) == Macro.to_string(actual)
end

test "returns a list of text and expr nodes for the class object syntax" do
Expand Down
17 changes: 17 additions & 0 deletions test/temple/renderer_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,23 @@ defmodule Temple.RendererTest do
assert expected == result
end

test "runtime boolean attributes emit the right values" do
truthy = true
falsey = false

result =
Renderer.compile do
input type: "text", disabled: falsey, checked: truthy, placeholder: "Enter some text..."
end

# html
expected = """
<input type="text" checked placeholder="Enter some text...">
"""

assert expected == result
end

test "multiple slots" do
assigns = %{}

Expand Down