Enable passing content or attrs as variables

Before, the guards in the macro definitions would only work for content
or attributes that were passed in as literals.
This commit is contained in:
Mitchell Hanberg 2019-05-08 03:31:13 -04:00
parent 6c6bd8dd3b
commit d81029c9be
2 changed files with 141 additions and 25 deletions

View file

@ -46,52 +46,68 @@ defmodule Dsl.Html do
el = unquote(el)
quote do
unquote(el)([], nil)
put_open_tag(var!(buff, Dsl.Html), unquote(el), [])
put_close_tag(var!(buff, Dsl.Html), unquote(el))
end
end
defmacro unquote(el)(attrs) when is_list(attrs) do
el = unquote(el)
{inner, attrs} = Keyword.pop(attrs, :do, nil)
quote do
unquote(el)(unquote(attrs), unquote(inner))
end
end
defmacro unquote(el)(content) when is_binary(content) do
defmacro unquote(el)([{:do, inner} | attrs]) do
el = unquote(el)
quote do
unquote(el)(unquote(content), [])
put_open_tag(var!(buff, Dsl.Html), unquote(el), unquote(attrs))
_ = unquote(inner)
put_close_tag(var!(buff, Dsl.Html), unquote(el))
end
end
defmacro unquote(el)(content, attrs) when not is_list(content) and is_list(attrs) do
defmacro unquote(el)(attrs_or_content) do
el = unquote(el)
text = {:text, [], [content]}
quote do
unquote(el)(unquote(attrs), unquote(text))
put_open_tag(var!(buff, Dsl.Html), unquote(el), unquote(attrs_or_content))
put_close_tag(var!(buff, Dsl.Html), unquote(el))
end
end
defmacro unquote(el)(attrs, inner) when is_list(attrs) do
defmacro unquote(el)(attrs, [{:do, inner}]) do
el = unquote(el)
quote do
attrs = unquote(attrs)
put_buffer(var!(buff, Dsl.Html), "<#{unquote(el)}#{compile_attrs(attrs)}>")
unquote(inner)
put_buffer(var!(buff, Dsl.Html), "</#{unquote(el)}>")
put_open_tag(var!(buff, Dsl.Html), unquote(el), attrs)
_ = unquote(inner)
put_close_tag(var!(buff, Dsl.Html), unquote(el))
end
end
defmacro unquote(el)(content, attrs) do
el = unquote(el)
quote do
attrs = unquote(attrs)
put_open_tag(var!(buff, Dsl.Html), unquote(el), attrs)
text unquote(content)
put_close_tag(var!(buff, Dsl.Html), unquote(el))
end
end
end
for el <- @void_elements do
defmacro unquote(el)(attrs \\ [])
def put_open_tag(buff, el, attrs) when is_list(attrs) do
put_buffer(buff, "<#{el}#{compile_attrs(attrs)}>")
end
defmacro unquote(el)(attrs) do
def put_open_tag(buff, el, content) when is_binary(content) do
put_buffer(buff, "<#{el}>")
put_buffer(buff, content)
end
def put_close_tag(buff, el) do
put_buffer(buff, "</#{el}>")
end
for el <- @void_elements do
defmacro unquote(el)(attrs \\ []) do
el = unquote(el)
quote do
@ -142,9 +158,7 @@ defmodule Dsl.Html do
defmacro defcomponent(name, do: block) do
quote do
defmacro unquote(name)(props \\ [])
defmacro unquote(name)(props) do
defmacro unquote(name)(props \\ []) do
outer = unquote(Macro.escape(block))
name = unquote(name)

View file

@ -56,8 +56,19 @@ defmodule Dsl.HtmlTest do
end
end
end
defcomponent :variable_as_prop do
div id: @bob
end
defcomponent :variable_as_prop_with_block do
div id: @bob do
@children
end
end
end
describe "non-void elements" do
test "renders two divs" do
{:safe, result} =
@ -79,6 +90,41 @@ defmodule Dsl.HtmlTest do
assert result == "<div></div><span></span>"
end
test "renders an el that taks attrs and a block" do
{:safe, result} =
htm do
div class: "bob" do
span()
span()
end
end
assert result == ~s{<div class="bob"><span></span><span></span></div>}
end
test "renders one els nested inside an el" do
{:safe, result} =
htm do
div do
span()
end
end
assert result == "<div><span></span></div>"
end
test "renders two els nested inside an el" do
{:safe, result} =
htm do
div do
span()
span()
end
end
assert result == "<div><span></span><span></span></div>"
end
test "renders two divs that are rendered by a loop" do
{:safe, result} =
htm do
@ -122,6 +168,20 @@ defmodule Dsl.HtmlTest do
assert result == ~s{<div class="hello"><div class="hi"></div></div>}
end
test "renders an attribute on a div passed as a variable" do
attrs1 = [class: "hello"]
attrs2 = [class: "hi"]
{:safe, result} =
htm do
div attrs1 do
div(attrs2)
end
end
assert result == ~s{<div class="hello"><div class="hi"></div></div>}
end
test "renders multiple attributes on a div without block" do
{:safe, result} =
htm do
@ -140,6 +200,18 @@ defmodule Dsl.HtmlTest do
assert result == ~s{<div>CONTENT</div><div class="hi">MORE</div>}
end
test "can accept content as first argument passed as a variable" do
content = "CONTENT"
more = "MORE"
{:safe, result} =
htm do
div(content)
div(more, class: "hi")
end
assert result == ~s{<div>CONTENT</div><div class="hi">MORE</div>}
end
end
describe "void elements" do
@ -286,6 +358,36 @@ defmodule Dsl.HtmlTest do
~s|<div>:atom</div><div>%{key: &quot;value&quot;}</div><div>{:status, :tuple}</div><div>&quot;string&quot;</div><div>1</div><div>[1, 2, 3]</div>|
end
test "can pass a variable as a prop" do
import Component
bob = "hi"
{:safe, result} =
htm do
variable_as_prop bob: bob
end
assert result ==
~s|<div id="hi"></div>|
end
test "can pass a variable as a prop to a component with a block" do
import Component
bob = "hi"
class = "joe"
{:safe, result} =
htm do
variable_as_prop_with_block bob: bob do
div()
end
end
assert result == ~s|<div id="hi"><div></div></div>|
end
test "can use string interpolation in props" do
interop = "hi"