Skip to content

Commit 59d04c4

Browse files
committed
feat(custom option tags): allow custom tags in options_for_select
1 parent 0229e74 commit 59d04c4

2 files changed

Lines changed: 56 additions & 10 deletions

File tree

lib/phoenix_html/form.ex

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ defmodule Phoenix.HTML.Form do
289289
an atom, string or integer to be used as the option value
290290
* simple atom, string or integer - which will be used as both label and value
291291
for the generated select
292-
292+
293293
## Option groups
294294
295295
If `options` is map or keyword list where the first element is a string,
@@ -320,6 +320,12 @@ defmodule Phoenix.HTML.Form do
320320
#=> <option>France</option>
321321
#=> </optgroup>
322322
323+
Custom option tags:
324+
325+
options_for_select(["Admin": "admin", "User": "user"], nil, tag: "opt")
326+
#=> <opt value="admin">Admin</opt>
327+
#=> <opt value="user">User</opt>
328+
323329
Horizontal separators can be added:
324330
325331
options_for_select(["Admin", "User", :hr, "New"], nil)
@@ -336,21 +342,22 @@ defmodule Phoenix.HTML.Form do
336342
337343
338344
"""
339-
def options_for_select(options, selected_values) do
345+
def options_for_select(options, selected_values, extra \\ []) do
340346
{:safe,
341347
escaped_options_for_select(
342348
options,
343-
selected_values |> List.wrap() |> Enum.map(&html_escape/1)
349+
selected_values |> List.wrap() |> Enum.map(&html_escape/1),
350+
extra
344351
)}
345352
end
346353

347-
defp escaped_options_for_select(options, selected_values) do
354+
defp escaped_options_for_select(options, selected_values, extra) do
348355
Enum.reduce(options, [], fn
349356
{:hr, nil}, acc ->
350357
[acc | hr_tag()]
351358

352359
{option_key, option_value}, acc ->
353-
[acc | option(option_key, option_value, [], selected_values)]
360+
[acc | option(option_key, option_value, extra, selected_values)]
354361

355362
options, acc when is_list(options) ->
356363
{option_key, options} =
@@ -373,27 +380,28 @@ defmodule Phoenix.HTML.Form do
373380
{value, options}
374381
end
375382

376-
[acc | option(option_key, option_value, options, selected_values)]
383+
[acc | option(option_key, option_value, extra ++ options, selected_values)]
377384

378385
:hr, acc ->
379386
[acc | hr_tag()]
380387

381388
option, acc ->
382-
[acc | option(option, option, [], selected_values)]
389+
[acc | option(option, option, extra, selected_values)]
383390
end)
384391
end
385392

386-
defp option(group_label, group_values, [], value)
393+
defp option(group_label, group_values, extra, value)
387394
when is_list(group_values) or is_map(group_values) do
388-
section_options = escaped_options_for_select(group_values, value)
395+
section_options = escaped_options_for_select(group_values, value, extra)
389396
option_tag("optgroup", [label: group_label], {:safe, section_options})
390397
end
391398

392399
defp option(option_key, option_value, extra, value) do
393400
option_key = html_escape(option_key)
394401
option_value = html_escape(option_value)
395402
attrs = extra ++ [selected: option_value in value, value: option_value]
396-
option_tag("option", attrs, option_key)
403+
{tag, attrs} = Keyword.pop(attrs, :tag, "option")
404+
option_tag(tag, attrs, option_key)
397405
end
398406

399407
defp option_tag(name, attrs, {:safe, body}) when is_binary(name) and is_list(attrs) do

test/phoenix_html/form_test.exs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,44 @@ defmodule Phoenix.HTML.FormTest do
295295
~s(<option value="new">New</option>)
296296
end
297297

298+
299+
test "with custom option tag" do
300+
assert options_for_select(["value", "novalue", nil], "novalue", tag: "el-option") |> safe_to_string() ==
301+
~s(<el-option value="value">value</el-option>) <>
302+
~s(<el-option selected value="novalue">novalue</el-option>) <>
303+
~s(<el-option value=""></el-option>)
304+
305+
assert options_for_select(["value", :hr, "novalue"], "novalue", tag: "el-option") |> safe_to_string() ==
306+
~s(<el-option value="value">value</el-option>) <>
307+
~s(<hr/>) <>
308+
~s(<el-option selected value="novalue">novalue</el-option>)
309+
310+
assert options_for_select(
311+
[
312+
[value: "value", key: "Value", disabled: true],
313+
:hr,
314+
[value: "novalue", key: "No Value"],
315+
[value: nil, key: nil]
316+
],
317+
"novalue",
318+
tag: "el-option"
319+
)
320+
|> safe_to_string() ==
321+
~s(<el-option disabled value="value">Value</el-option>) <>
322+
~s(<hr/>) <>
323+
~s(<el-option selected value="novalue">No Value</el-option>) <>
324+
~s(<el-option value=""></el-option>)
325+
326+
assert options_for_select(~w(value novalue), ["value", "novalue"], tag: "el-option") |> safe_to_string() ==
327+
~s(<el-option selected value="value">value</el-option>) <>
328+
~s(<el-option selected value="novalue">novalue</el-option>)
329+
330+
assert options_for_select([Label: "value", hr: nil, New: "new"], nil, tag: "el-option") |> safe_to_string() ==
331+
~s(<el-option value="value">Label</el-option>) <>
332+
~s(<hr/>) <>
333+
~s(<el-option value="new">New</el-option>)
334+
end
335+
298336
test "with groups" do
299337
assert options_for_select([{"foo", ["bar", :hr, "baz"]}, {"qux", ~w(qux quz)}], "qux")
300338
|> safe_to_string() ==

0 commit comments

Comments
 (0)