Skip to content
Permalink
Browse files

Merge pull request #45 from geolessel/more-options

feature(react_component): allow `html_element` and other options
  • Loading branch information
geolessel committed Nov 25, 2019
2 parents 47f7890 + 9f47588 commit fbbbf5f9f2c8cfd8a92010a472c15207034fb99c
Showing with 87 additions and 19 deletions.
  1. +1 −1 README.md
  2. +31 −4 lib/react_phoenix/client_side.ex
  3. +1 −1 mix.exs
  4. +1 −1 package.json
  5. +1 −1 priv/js/react_phoenix.js
  6. +52 −11 test/react_phoenix/client_side_test.exs
@@ -32,7 +32,7 @@ dependencies in `mix.exs`:
```elixir
def deps do
[
{:react_phoenix, "~> 1.0.1"}
{:react_phoenix, "~> 1.1.0"}
]
end
```
@@ -76,14 +76,41 @@ defmodule ReactPhoenix.ClientSide do
The resulting `<div>` tag is formatted specifically for the included javascript
helper to then turn into your named React component and then pass in the props specified.
## Options
You can pass a Keyword list of options that controls the rendering of the final html element
that the react component will be rendered into.
* `target_id` - If you have a rendered html element already on your page with a unique `id`
attribute, this will render your react component into _that_ already-existing element
instead of creating one of its own. The default is an empty string, which will cause it
to render into its own created element.
* `html_element` - By default, `ReactPhoenix.ClientSide.react_component/3` will create a
`div` for output in your template. If you'd rather use a different element (`span`, `ul`, etc.),
you can specify it here as an atom (e.g. `:span`, `:ul`, etc.).
Anything else you pass in will be further passed directly to the html element it
creates as attributes. This allows you more control over the element. Some useful examples
of what you would specify are `class`, `id`, `style`, etc.
"""
@spec react_component(name :: String.t(), props :: map, opts :: [target_id: String.t()]) ::
@spec react_component(name :: String.t(), props :: map, opts :: list()) ::
Phoenix.HTML.safe()
def react_component(name, props, opts) when is_map(props) do
props = Jason.encode!(props)

content_tag(:div, "", [
{:data, [react_class: name, react_props: props, react_target_id: opts[:target_id]]}
])
{html_element, opts} = Keyword.pop(opts, :html_element, :div)
{target_id, opts} = Keyword.pop(opts, :target_id, "")

content_tag(
html_element,
"",
Keyword.merge(
[
{:data, [react_class: name, react_props: props, react_target_id: target_id]}
],
opts
)
)
end
end
@@ -1,7 +1,7 @@
defmodule ReactPhoenix.Mixfile do
use Mix.Project

@version "1.0.1"
@version "1.1.0"
@source_url "https://github.com/geolessel/react-phoenix"

def project do
@@ -1,6 +1,6 @@
{
"name": "react-phoenix",
"version": "1.0.1",
"version": "1.1.0",
"scripts": {
"release": "node ./node_modules/.bin/babel src/react_phoenix.js | node ./node_modules/uglify-js/bin/uglifyjs - --mangle --compress --output priv/js/react_phoenix.js"
},

Some generated files are not rendered by default. Learn more.

@@ -3,44 +3,85 @@ defmodule ReactPhoenix.ClientSideTest do
doctest ReactPhoenix.ClientSide
import ReactPhoenix.ClientSide

test "react_component returns a safe tuple" do
test "react_component/1 returns a safe tuple" do
assert {:safe, _} = react_component("test")
end

test "react_component returns a renderable div" do
html = Phoenix.HTML.safe_to_string(react_component("test"))
test "react_component/1 returns a renderable div" do
html =
"test"
|> react_component()
|> Phoenix.HTML.safe_to_string()

assert String.match?(html, ~r|^<div.*></div>$|)
end

test "react_component returns a renderable div with data-react-class containing component name" do
html = Phoenix.HTML.safe_to_string(react_component("test"))
test "react_component/1 returns a renderable div with data-react-class containing component name" do
html =
"test"
|> react_component()
|> Phoenix.HTML.safe_to_string()

assert String.match?(html, ~r|data-react-class="test"|)
end

test "react_component returns a renderable div with data-react-props containing props list" do
html = Phoenix.HTML.safe_to_string(react_component("test", my: "props"))
test "react_component/2 returns a renderable div with data-react-props containing props list" do
html =
"test"
|> react_component(my: "props")
|> Phoenix.HTML.safe_to_string()

expected =
"<div data-react-class=\"test\" data-react-props=\"{&quot;my&quot;:&quot;props&quot;}\"></div>"

assert html == expected
end

test "react_component returns a renderable div with data-react-props containing props map" do
html = Phoenix.HTML.safe_to_string(react_component("test", %{my: "props"}))
test "react_component/2 returns a renderable div with data-react-props containing props map" do
html =
"test"
|> react_component(%{my: "props"})
|> Phoenix.HTML.safe_to_string()

expected =
"<div data-react-class=\"test\" data-react-props=\"{&quot;my&quot;:&quot;props&quot;}\"></div>"

assert html == expected
end

test "react_component accepts a target div option" do
html = Phoenix.HTML.safe_to_string(react_component("test", %{}, target_id: "test-id"))
test "react_component/3 accepts a target div option" do
html =
"test"
|> react_component(%{}, target_id: "test-id")
|> Phoenix.HTML.safe_to_string()

expected =
"<div data-react-class=\"test\" data-react-props=\"{}\" data-react-target-id=\"test-id\"></div>"

assert html == expected
end

test "react_component/3 accepts a html_element option" do
html =
"test"
|> react_component(%{}, html_element: :span)
|> Phoenix.HTML.safe_to_string()

expected =
"<span data-react-class=\"test\" data-react-props=\"{}\" data-react-target-id=\"\"></span>"

assert html == expected
end

test "react_component/3 passes on extra options to the html element" do
html =
"test"
|> react_component(%{}, class: "row", id: "content")
|> Phoenix.HTML.safe_to_string()

expected =
"<div class=\"row\" data-react-class=\"test\" data-react-props=\"{}\" data-react-target-id=\"\" id=\"content\"></div>"

assert html == expected
end
end

0 comments on commit fbbbf5f

Please sign in to comment.
You can’t perform that action at this time.