Skip to content

Commit

Permalink
Add a wrapping div to inputs
Browse files Browse the repository at this point in the history
  • Loading branch information
drapergeek committed Mar 9, 2018
1 parent 21479e1 commit 139f93b
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 53 deletions.
3 changes: 2 additions & 1 deletion README.md
Expand Up @@ -67,7 +67,8 @@ the application config.
config :formulator,
translate_error_module: YourAppName.ErrorHelpers,
validate: false, # defaults to true
validate_regex: false # defaults to true
validate_regex: false, # defaults to true
wrapping_class: "input-set" # defaults to nil
```

You can import the package into all your views or individually as it makes
Expand Down
82 changes: 58 additions & 24 deletions lib/formulator.ex
Expand Up @@ -22,74 +22,108 @@ defmodule Formulator do
this will add a pattern HTML5 validation. This should work with most simple
regex patterns, but the browser's regex engine may differ from Erlang's.
* `:wrapper_class` - This allows you to add a class to the div
that wraps the input and label. This can also be set in your config:
`config :formulator, wrapper_class: "input-wrapper"
## Examples
Basic input:
<%= input form, :name %>
#=> <label for="user_name">Name</label>
#=> <input id="user_name" name="user[name]" type="text" value="">
#=> <div>
#=> <label for="user_name">Name</label>
#=> <input id="user_name" name="user[name]" type="text" value="">
#=> </div>
Without a label:
<%= input form, :name, label: false %>
#=> <input id="user_name" name="user[name]" aria-label="name" type="text" value="">
#=> <div>
#=> <input id="user_name" name="user[name]" aria-label="name" type="text" value="">
#=> </div>
Passing other options:
<%= input form, :name, label: [class: "control-label"] %>
#=> <label class="control-label" for="user_name">Name</label>
#=> <input id="user_name" type="text" name="user[name]" value="">
#=> <div>
#=> <label class="control-label" for="user_name">Name</label>
#=> <input id="user_name" type="text" name="user[name]" value="">
#=> </div>
Using different input types:
<%= input form, :email_address,
as: :email,
placeholder: "your@email.com",
class: "my-email-class",
label: [class: "my-email-label-class"] %>
#=> <label
class="my-email-label-class"
for="user_email_address">Email Address</label>
#=> <input
id="user_email_address"
type="email"
name="user[email_address]"
placeholder: "your@email.com"
value=""
class="my-email-class">
#=> <div>
#=> <label
class="my-email-label-class"
for="user_email_address">Email Address</label>
#=> <input
id="user_email_address"
type="email"
name="user[email_address]"
placeholder: "your@email.com"
value=""
class="my-email-class">
#=> </div>
Or a number input:
<%= input form, :count, as: :number %>
#=> <label for="asset_count">Count</label>
#=> <input id="asset_count" type="number" name="asset[count]" value="">
#=> <div>
#=> <label for="asset_count">Count</label>
#=> <input id="asset_count" type="number" name="asset[count]" value="">
#=> <div>
If your form is using a changeset with validations (eg, with `Ecto` and `phoenix_ecto`),
then Formulator will add HTML5 validation attributes:
<%= input form, :email, as: :email %>
#=> <label for="user_email">Email</label>
#=> <input id="user_email" type="email" name="user[email]" required="required" pattern=".+@.+" %>
#=> <div>
#=> <label for="user_email">Email</label>
#=> <input id="user_email" type="email" name="user[email]" required="required" pattern=".+@.+" %>
#=> <div>
If you would rather not add HTML5 validation attributes, you can opt out
by supplying `validate: false`:
<%= input form, :email, as: :email, validate: false %>
#=> <label for="user_email">Email</label>
#=> <input id="user_email" type="email" name="user[email]" %>
#=> <div>
#=> <label for="user_email">Email</label>
#=> <input id="user_email" type="email" name="user[email]" %>
#=> </div>
You may want HTML5 validations, but the browser's regex engine is not
working with Elixir's regex engine. You can opt-out of regex validation
with `validate_regex: false`:
<%= input form, :email, as: :email, validate_regex: false %>
#=> <label for="user_email">Email</label>
#=> <input id="user_email" type="email" name="user[email]" required="required" %>
#=> <div>
#=> <label for="user_email">Email</label>
#=> <input id="user_email" type="email" name="user[email]" required="required" %>
#=> </div>
"""

@spec input(Phoenix.HTML.Form.t, atom, []) :: binary
def input(form, field, options \\ []) do
{label_options, options} = extract_label_options(options)
form
|> build_input_and_associated_tags(field, options)
|> add_wrapper(options)
end

defp build_input_and_associated_tags(form, field, options) do
{label_options, options} = extract_label_options(options)
case label_options do
false -> input_without_label(form, field, options)
_ -> input_with_label(form, field, label_options, options)
end
end

defp add_wrapper(html, options) do
[content_tag(:div, html, class: wrapper_class(options))]
end

defp wrapper_class(options) do
options[:wrapper_class] || Application.get_env(:formulator, :wrapper_class)
end

defp extract_label_options(options) do
label_options = options |> Keyword.get(:label, [])
options = options |> Keyword.delete(:label)
Expand Down
78 changes: 50 additions & 28 deletions test/formulator_test.exs
Expand Up @@ -5,34 +5,60 @@ defmodule FormulatorTest do
alias Formulator.Test.SampleSchema

describe "input - label" do
test "a wrapping div is adding for the fields" do
div_element =
%{name: ""}
|> prepare_form
|> Formulator.input(:name, wrapper_class: "my-wrapper")
|> extract_html
|> to_string

assert div_element =~ ~s(<div class="my-wrapper">)
end

test "allows configuring the wrapping class in the config" do
Application.put_env(:formulator, :wrapper_class, "config-wrapper")

div_element =
%{name: ""}
|> prepare_form
|> Formulator.input(:name)
|> extract_html
|> to_string

assert div_element =~ ~s(<div class="config-wrapper">)

Application.delete_env(:formulator, :wrapper_class)
end

test "a label is created with the field name" do
label =
%{name: ""}
|> prepare_form
|> Formulator.input(:name)
|> extract_element(:first)
|> extract_html
|> to_string

assert label == ~s(<label for="_name">Name</label>)
assert label =~ ~s(<label for="_name">Name</label>)
end

test "passing the label: [text:] option allows for overriding the label" do
label =
%{name: ""}
|> prepare_form
|> Formulator.input(:name, label: [text: "Customer Name"])
|> extract_element(:first)
|> extract_html
|> to_string

assert label == ~s(<label for="_name">Customer Name</label>)
assert label =~ ~s(<label for="_name">Customer Name</label>)
end

test "passing `label: false` will add `aria-label` to the input" do
input =
%{last_name: ""}
|> prepare_form
|> Formulator.input(:last_name, label: false)
|> extract_element(:first)
|> extract_html
|> to_string

assert input =~ ~s(aria-label="Last name")
Expand All @@ -43,21 +69,21 @@ defmodule FormulatorTest do
%{name: ""}
|> prepare_form
|> Formulator.input(:name, label: [class: "control-label"])
|> extract_element(:first)
|> extract_html
|> to_string

assert label == ~s(<label class="control-label" for="_name">Name</label>)
assert label =~ ~s(<label class="control-label" for="_name">Name</label>)
end

test "passing the label: 'text' option allows for overriding the label" do
label =
%{name: ""}
|> prepare_form()
|> Formulator.input(:name, label: "Customer Name")
|> extract_element(:first)
|> extract_html
|> to_string

assert label == ~s(<label for="_name">Customer Name</label>)
assert label =~ ~s(<label for="_name">Customer Name</label>)
end
end

Expand All @@ -67,7 +93,7 @@ defmodule FormulatorTest do
%{name: ""}
|> prepare_form
|> Formulator.input(:name, class: "customer_name")
|> extract_element(:second)
|> extract_html
|> to_string

assert input =~ ~s(class="customer_name")
Expand All @@ -78,7 +104,7 @@ defmodule FormulatorTest do
%{name: ""}
|> prepare_form
|> Formulator.input(:name, as: :hidden)
|> extract_element(:second)
|> extract_html
|> to_string

assert input =~ ~s(type="hidden")
Expand All @@ -89,7 +115,7 @@ defmodule FormulatorTest do
%{admin: ""}
|> prepare_form()
|> Formulator.input(:admin, as: :checkbox, class: "foo")
|> extract_element(:second)
|> extract_html
|> to_string

assert input =~ ~s(type="checkbox")
Expand All @@ -103,7 +129,7 @@ defmodule FormulatorTest do
|> Formulator.input(:date, as: :date, builder: fn b ->
b.(:year, [options: 2017..2021, class: "foo"])
end)
|> extract_element(:second)
|> extract_html
|> to_string

assert input =~ ~s(id="_date_year")
Expand All @@ -117,7 +143,7 @@ defmodule FormulatorTest do
|> Formulator.input(:datetime, as: :datetime, builder: fn b ->
b.(:year, [options: 2017..2021, class: "foo"])
end)
|> extract_element(:second)
|> extract_html
|> to_string

assert input =~ ~s(id="_datetime_year")
Expand All @@ -131,7 +157,7 @@ defmodule FormulatorTest do
|> Formulator.input(:time, as: :time, builder: fn b ->
b.(:hour, [options: 1..2, class: "foo"])
end)
|> extract_element(:second)
|> extract_html
|> to_string

assert input =~ ~s(id="_time_hour")
Expand All @@ -143,7 +169,7 @@ defmodule FormulatorTest do
%{name: ""}
|> prepare_form()
|> Formulator.input(:name, as: :textarea, class: "foo")
|> extract_element(:second)
|> extract_html
|> to_string

assert input =~ ~s(textarea)
Expand All @@ -157,7 +183,7 @@ defmodule FormulatorTest do
%{name: "Fluff"}
|> prepare_changeset_form(:validate)
|> Formulator.input(:name, validate: true)
|> extract_element(:second)
|> extract_html
|> to_string

assert input =~ ~s(required)
Expand All @@ -168,7 +194,7 @@ defmodule FormulatorTest do
%{name: "Fluff"}
|> prepare_changeset_form(:novalidate)
|> Formulator.input(:name, validate: true)
|> extract_element(:second)
|> extract_html
|> to_string

refute input =~ ~s(required)
Expand All @@ -181,7 +207,7 @@ defmodule FormulatorTest do
%{number: "321"}
|> prepare_changeset_form(:validate)
|> Formulator.input(:number, validate: true)
|> extract_element(:second)
|> extract_html
|> to_string

assert input =~ ~s(min)
Expand All @@ -192,7 +218,7 @@ defmodule FormulatorTest do
%{number: "321"}
|> prepare_changeset_form(:novalidate)
|> Formulator.input(:number, validate: true)
|> extract_element(:second)
|> extract_html
|> to_string

refute input =~ ~s(min)
Expand All @@ -205,7 +231,7 @@ defmodule FormulatorTest do
%{email: "test@domain.com"}
|> prepare_changeset_form(:validate)
|> Formulator.input(:email_address, validate_regex: true)
|> extract_element(:second)
|> extract_html
|> to_string

assert input =~ ~s(pattern)
Expand All @@ -216,7 +242,7 @@ defmodule FormulatorTest do
%{email: "test@domain.com"}
|> prepare_changeset_form(:novalidate)
|> Formulator.input(:email_address, validate_regex: true)
|> extract_element(:second)
|> extract_html
|> to_string

refute input =~ ~s(pattern)
Expand All @@ -229,7 +255,7 @@ defmodule FormulatorTest do
%{email: "test_domain.com"}
|> prepare_conn_form
|> Formulator.input(:email_address, validate_regex: true)
|> extract_element(:second)
|> extract_html
|> to_string

refute input =~ ~s(pattern)
Expand All @@ -240,11 +266,7 @@ defmodule FormulatorTest do
%Form{data: attrs}
end

defp extract_element(form, :second) do
[_, {:safe, element} | _] = form
element
end
defp extract_element(form, :first) do
defp extract_html(form) do
[{:safe, element} | _] = form
element
end
Expand Down

0 comments on commit 139f93b

Please sign in to comment.