diff --git a/Project.toml b/Project.toml
index 4cf31c59..6b3da832 100644
--- a/Project.toml
+++ b/Project.toml
@@ -34,9 +34,3 @@ MIMEs = "0.1"
Reexport = "^1"
URIs = "1"
julia = "1.4"
-
-[extras]
-Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
-
-[targets]
-test = ["Test"]
diff --git a/src/Builtins.jl b/src/Builtins.jl
index 1b70e6e3..61727461 100644
--- a/src/Builtins.jl
+++ b/src/Builtins.jl
@@ -1,5 +1,5 @@
### A Pluto.jl notebook ###
-# v0.19.14
+# v0.19.19
using Markdown
using InteractiveUtils
@@ -832,18 +832,6 @@ begin
OldMultiSelect
end
-# ╔═╡ f21db694-2acb-417d-9f4d-0d2400aa067e
-subarrays(x) = (
- x[collect(I)]
- for I in Iterators.product(Iterators.repeated([true,false],length(x))...) |> collect |> vec
-)
-
-# ╔═╡ 4d8ea460-ff2b-4e92-966e-89e76d4806af
-# ╠═╡ skip_as_script = true
-#=╠═╡
-subarrays([2,3,3]) |> collect
- ╠═╡ =#
-
# ╔═╡ e058076f-46fc-4435-ab45-530e27c95478
begin
local result = begin
@@ -1231,116 +1219,6 @@ begin
result
end
-# ╔═╡ 38a7533e-7b0f-4c55-ade5-5a8d879d14c7
-begin
- local result = begin
- """
- ```julia
- MultiSelect(options::Vector; [default], [size::Int])
- # or with a custom display value:
- MultiSelect(options::Vector{Pair{Any,String}}; [default], [size::Int])
- ```
-
- A multi-selector - the user can choose one or more of the `options`.
-
- See [`Select`](@ref) for a version that allows only one selected item.
-
- # Examples
- ```julia
- @bind vegetables MultiSelect(["potato", "carrot"])
-
- if "carrot" ∈ vegetables
- "yum yum!"
- end
- ```
-
- ```julia
- @bind chosen_functions MultiSelect([sin, cos, tan, sqrt])
-
- [f(0.5) for f in chosen_functions]
- ```
-
- You can also specify a display value by giving pairs `bound_value => display_value`:
-
- ```julia
- @bind chosen_functions MultiSelect([
- cos => "cosine function",
- sin => "sine function",
- ])
-
- [f(0.5) for f in chosen_functions]
- ```
-
- The `size` keyword argument may be used to specify how many rows should be visible at once.
-
- ```julia
- @bind letters MultiSelect(string.('a':'z'), size=20)
- ```
- """
- struct MultiSelect{BT,DT}
- options::AbstractVector{Pair{BT,DT}}
- default::Union{Missing, AbstractVector{BT}}
- size::Union{Missing,Int}
- end
- end
- MultiSelect(options::AbstractVector{<:Pair{BT,DT}}; default=missing, size=missing) where {BT,DT} = MultiSelect(options, default, size)
- MultiSelect(options::AbstractVector{BT}; default=missing, size=missing) where BT = MultiSelect{BT,BT}(Pair{BT,BT}[o => o for o in options], default, size)
-
- function Base.show(io::IO, m::MIME"text/html", select::MultiSelect)
-
- # compat code
- if !AbstractPlutoDingetjes.is_supported_by_display(io, Bonds.transform_value)
- compat_element = try
- OldMultiSelect(select.options, select.default, select.size)
- catch
- HTML("❌ You need to update Pluto to use this PlutoUI element.")
- end
- return show(io, m, compat_element)
- end
-
-
- show(io, m, @htl(
- """"""))
- end
-
-
- Base.get(select::MultiSelect) = Bonds.initial_value(select)
- Bonds.initial_value(select::MultiSelect{BT,DT}) where {BT,DT} =
- ismissing(select.default) ? BT[] : select.default
- Bonds.possible_values(select::MultiSelect) =
- subarrays((string(i) for i in 1:length(select.options)))
-
- function Bonds.transform_value(select::MultiSelect{BT,DT}, val_from_js) where {BT,DT}
- # val_from_js will be a vector of Strings, but let's allow Integers as well, there's no harm in that
- @assert val_from_js isa Vector
-
- val_nums = (
- v isa Integer ? v : tryparse(Int64, v)
- for v in val_from_js
- )
-
- BT[select.options[v].first for v in val_nums]
- end
-
- function Bonds.validate_value(select::MultiSelect, val)
- val isa Vector && all(val_from_js) do v
- val_num = v isa Integer ? v : tryparse(Int64, v)
- 1 ≤ val_num ≤ length(select.options)
- end
- end
-
- result
- end
-
# ╔═╡ c2b473f4-b56b-4a91-8377-6c86da895cbe
# ╠═╡ skip_as_script = true
#=╠═╡
@@ -1613,43 +1491,6 @@ r1
push!(r1s, r1)
╠═╡ =#
-# ╔═╡ 998a3bd7-2d09-4b3f-8a41-50736b666dea
-# ╠═╡ skip_as_script = true
-#=╠═╡
-MultiSelect(["a" => "🆘", "b" => "✅", "c" => "🆘", "d" => "✅", "c" => "🆘2", "c3" => "🆘"]; default=["b","d"])
- ╠═╡ =#
-
-# ╔═╡ 78473a2f-0a64-4aa5-a60a-94031a4167b8
-#=╠═╡
-bms = @bind ms1 MultiSelect(["a" => "default", teststr => teststr])
- ╠═╡ =#
-
-# ╔═╡ 43f86637-9f0b-480c-826a-bbf583e44646
-#=╠═╡
-bms
- ╠═╡ =#
-
-# ╔═╡ b6697df5-fd21-4553-9e90-1d33c0b51f70
-#=╠═╡
-ms1
- ╠═╡ =#
-
-# ╔═╡ 7bffc5d6-4056-4060-903e-7a1f73b6a8a0
-# ╠═╡ skip_as_script = true
-#=╠═╡
-@bind fs MultiSelect([sin, cos, tan])
- ╠═╡ =#
-
-# ╔═╡ 7f112de0-2678-4793-a25f-42e7495e6590
-#=╠═╡
-fs
- ╠═╡ =#
-
-# ╔═╡ 8fd52496-d4c9-4106-8a97-f19f1d8d8b0f
-#=╠═╡
-[f(0.5) for f in fs]
- ╠═╡ =#
-
# ╔═╡ a03af14a-e030-4ac1-b61a-0275c9956454
# ╠═╡ skip_as_script = true
#=╠═╡
@@ -1973,16 +1814,6 @@ export Slider, NumberField, Button, LabelButton, CounterButton, CheckBox, TextFi
# ╠═69a94f6a-420a-4587-bbad-1219a390862d
# ╠═d9522557-07e6-4a51-ae92-3abe7a7d2732
# ╟─cc80b7eb-ca09-41ca-8015-933591378437
-# ╟─38a7533e-7b0f-4c55-ade5-5a8d879d14c7
-# ╟─f21db694-2acb-417d-9f4d-0d2400aa067e
-# ╠═4d8ea460-ff2b-4e92-966e-89e76d4806af
-# ╠═78473a2f-0a64-4aa5-a60a-94031a4167b8
-# ╠═43f86637-9f0b-480c-826a-bbf583e44646
-# ╠═b6697df5-fd21-4553-9e90-1d33c0b51f70
-# ╠═998a3bd7-2d09-4b3f-8a41-50736b666dea
-# ╠═7bffc5d6-4056-4060-903e-7a1f73b6a8a0
-# ╠═7f112de0-2678-4793-a25f-42e7495e6590
-# ╠═8fd52496-d4c9-4106-8a97-f19f1d8d8b0f
# ╟─e058076f-46fc-4435-ab45-530e27c95478
# ╠═a03af14a-e030-4ac1-b61a-0275c9956454
# ╠═d4a0e98d-666c-4588-8499-f253a309a403
diff --git a/src/MultiCheckBox.jl b/src/MultiCheckBox.jl
index bc03c3ae..e4df0554 100644
--- a/src/MultiCheckBox.jl
+++ b/src/MultiCheckBox.jl
@@ -1,5 +1,5 @@
### A Pluto.jl notebook ###
-# v0.19.12
+# v0.19.19
using Markdown
using InteractiveUtils
@@ -38,23 +38,181 @@ md"""
animals_count = Ref(0)
╠═╡ =#
+# ╔═╡ ba2cf480-ff5e-43a3-8cba-5a4754b776f5
+md"""
+# MultiSelect
+"""
+
+# ╔═╡ 775c49e9-bb4d-42ca-8ed0-d259d0d18725
+teststr = "\"\" woa"
+
# ╔═╡ c8350f43-0d30-45d0-873b-ff56c5801ac1
md"""
-## Definition
+## Definitions
"""
+# ╔═╡ 693f0bc4-2076-44e4-84f4-5ef1f3f43dd7
+import AbstractPlutoDingetjes
+
# ╔═╡ 631c14bf-e2d3-4a24-8ddc-095a3dab80ef
import AbstractPlutoDingetjes.Bonds
+# ╔═╡ a666d4df-dbbb-4837-b850-363caa576fe9
+function initially_selected(options, defaults)
+ # Given pairs of `bound_value => display_value`
+ # (for MultiSelect or MultiCheckBox),
+ # and list of default bound values,
+ # return a bool array of which options should be initially selected to result
+ # in the supplied defaults as the widget's value.
+ # This is a bit tricky due to the possibility of duplicate bound values.
+ # See https://github.com/JuliaPluto/PlutoUI.jl/issues/106
+ defaults_copy = copy(defaults)
+ [
+ let
+ i = findfirst(isequal(k), defaults_copy)
+ if i === nothing
+ false
+ else
+ deleteat!(defaults_copy, i)
+ true
+ end
+ end
+ for (k,v) in options]
+end
+
# ╔═╡ c38de38d-e900-4309-a9f6-1392af2f245b
subarrays(x) = (
x[collect(I)]
for I in Iterators.product(Iterators.repeated([true,false],length(x))...) |> collect |> vec
)
+# ╔═╡ cd217cac-658a-44b7-8c88-348ae1af9b64
+subarrays([2,3,3]) |> collect
+
+# ╔═╡ 2c634e7f-428f-4655-951b-2a8e4efce548
+multi_possible_values(options) = subarrays(string.(eachindex(options)))
+
+# ╔═╡ d7d9f4f3-4bac-4431-b3af-0af651a483b7
+function multi_transform_value(options, val_from_js)
+ # val_from_js will be a vector of Strings, but let's allow Integers as well, there's no harm in that
+ @assert val_from_js isa Vector
+
+ val_nums = (
+ v isa Integer ? v : tryparse(Int64, v)
+ for v in val_from_js
+ )
+
+ [options[v].first for v in val_nums]
+end
+
+# ╔═╡ 9cd42452-149e-4080-972f-a31f7023f67d
+function multi_validate_value(options, val_from_js)
+ allunique(val_from_js) && all(val_from_js) do v
+ val_num = v isa Integer ? v : tryparse(Int64, v)
+ 1 ≤ val_num ≤ length(options)
+ end
+end
+
+# ╔═╡ 41515a2f-81d7-4a47-8139-e0e4096c8cd4
+begin
+ local result = begin
+ """
+ ```julia
+ MultiSelect(options::Vector; [default], [size::Int])
+ # or with a custom display value:
+ MultiSelect(options::Vector{Pair}; [default], [size::Int])
+ ```
+
+ A multi-selector - the user can choose one or more of the `options`.
+
+ See [`Select`](@ref) for a version that allows only one selected item.
+
+ # Examples
+ ```julia
+ @bind vegetables MultiSelect(["potato", "carrot"])
+
+ if "carrot" ∈ vegetables
+ "yum yum!"
+ end
+ ```
+
+ ```julia
+ @bind chosen_functions MultiSelect([sin, cos, tan, sqrt])
+
+ [f(0.5) for f in chosen_functions]
+ ```
+
+ You can also specify a display value by giving pairs `bound_value => display_value`:
+
+ ```julia
+ @bind chosen_functions MultiSelect([
+ cos => "cosine function",
+ sin => "sine function",
+ ])
+
+ [f(0.5) for f in chosen_functions]
+ ```
+
+ The `size` keyword argument may be used to specify how many rows should be visible at once.
+
+ ```julia
+ @bind letters MultiSelect(string.('a':'z'), size=20)
+ ```
+ """
+ struct MultiSelect{BT,DT}
+ options::AbstractVector{Pair{BT,DT}}
+ default::Vector{BT}
+ size::Int
+
+ MultiSelect{BT,DT}(options::AbstractVector{<:Pair{BT,DT}}, default, size) where {BT,DT} = new{BT,DT}(options, default, coalesce(size, min(10, length(options))))
+ end
+ end
+ MultiSelect(options::AbstractVector{<:Pair{BT,DT}}; default = BT[], size = missing) where {BT,DT} = MultiSelect{BT,DT}(options, default, size)
+ MultiSelect(options::AbstractVector{BT}; default = BT[], size = missing) where BT = MultiSelect{BT,BT}(Pair{BT,BT}[o => o for o in options], default, size)
+
+ function Base.show(io::IO, m::MIME"text/html", select::MultiSelect)
+
+ # compat code
+ if !AbstractPlutoDingetjes.is_supported_by_display(io, Bonds.transform_value)
+ compat_element = try
+ OldMultiSelect(select.options, select.default, select.size)
+ catch
+ HTML("❌ You need to update Pluto to use this PlutoUI element.")
+ end
+ return show(io, m, compat_element)
+ end
+
+ selected = initially_selected(select.options, select.default)
+
+ show(io, m, @htl(
+ """"""))
+ end
+
+ Base.get(select::MultiSelect) = Bonds.initial_value(select)
+ Bonds.initial_value(select::MultiSelect) = select.default
+ Bonds.possible_values(select::MultiSelect) = multi_possible_values(select.options)
+ Bonds.transform_value(select::MultiSelect, val_from_js) = multi_transform_value(select.options, val_from_js)
+ Bonds.validate_value(select::MultiSelect, val_from_js) = multi_validate_value(select.options, val_from_js)
+
+ result
+end
+
+# ╔═╡ 112a576c-66fa-4336-a538-3843b8306587
+MultiSelect(["a" => "🆘", "b" => "✅", "c" => "🆘", "d" => "✅", "c" => "🆘2", "c3" => "🆘"]; default=["b","d"])
+
+# ╔═╡ 11fbe522-4596-418c-b1d4-076a83a8408b
+MultiSelect([[1 => "1.$i" for i=1:5]; [2 => "2.$i" for i=1:50]]; default=[1,1,1,2,2])
+
# ╔═╡ 430e2c1a-832f-11eb-024a-13e3989fd7c2
begin
- export MultiCheckBox
+ export MultiCheckBox, MultiSelect
local result = begin
"""
```julia
@@ -66,7 +224,7 @@ begin
See also: [`MultiSelect`](@ref).
- `options` can also be an array of pairs `key::Any => value::String`. The `key` is returned via `@bind`; the `value` is shown.
+ `options` can also be an array of pairs `key => value`. The `key` is returned via `@bind`; the `value` is shown.
# Keyword arguments
- `defaults` specifies which options should be checked initally.
@@ -98,37 +256,31 @@ begin
"""
struct MultiCheckBox{BT,DT}
options::AbstractVector{Pair{BT,DT}}
- default::Union{Missing,AbstractVector{BT}}
+ default::Vector{BT}
orientation::Symbol
select_all::Bool
+
+ function MultiCheckBox{BT,DT}(options::AbstractVector{<:Pair{BT,DT}}, default, orientation, select_all) where {BT,DT}
+ @assert orientation == :column || orientation == :row "Invalid orientation $(mc.orientation). Orientation should be :row or :column"
+ new{BT,DT}(options, default, orientation, select_all)
+ end
end
end
- MultiCheckBox(options::AbstractVector{<:Pair{BT,DT}}; default=missing, orientation=:row, select_all=false) where {BT,DT} = MultiCheckBox(options, default, orientation, select_all)
+ MultiCheckBox(options::AbstractVector{<:Pair{BT,DT}}; default=BT[], orientation=:row, select_all=false) where {BT,DT} = MultiCheckBox{BT,DT}(options, default, orientation, select_all)
- MultiCheckBox(options::AbstractVector{BT}; default=missing, orientation=:row, select_all=false) where BT = MultiCheckBox{BT,BT}(Pair{BT,BT}[o => o for o in options], default, orientation, select_all)
-
- function Base.show(io::IO, m::MIME"text/html", mc::MultiCheckBox)
- @assert mc.orientation == :column || mc.orientation == :row "Invalid orientation $(mc.orientation). Orientation should be :row or :column"
-
- defaults = coalesce(mc.default, [])
-
- # Old:
- # checked = [k in defaults for (k,v) in mc.options]
- #
- # More complicated to fix https://github.com/JuliaPluto/PlutoUI.jl/issues/106
- defaults_copy = copy(defaults)
- checked = [
- let
- i = findfirst(isequal(k), defaults_copy)
- if i === nothing
- false
- else
- deleteat!(defaults_copy, i)
- true
- end
- end
- for (k,v) in mc.options]
+ MultiCheckBox(options::AbstractVector{BT}; default=BT[], orientation=:row, select_all=false) where BT = MultiCheckBox{BT,BT}(Pair{BT,BT}[o => o for o in options], default, orientation, select_all)
+
+
+ Base.get(select::MultiCheckBox) = Bonds.initial_value(select)
+ Bonds.initial_value(select::MultiCheckBox) = select.default
+ Bonds.possible_values(select::MultiCheckBox) = multi_possible_values(select.options)
+ Bonds.transform_value(select::MultiCheckBox, val_from_js) = multi_transform_value(select.options, val_from_js)
+ Bonds.validate_value(select::MultiCheckBox, val_from_js) = multi_validate_value(select.options, val_from_js)
+
+ function Base.show(io::IO, m::MIME"text/html", mc::MultiCheckBox)
+
+ checked = initially_selected(mc.options, mc.default)
show(io, m, @htl("""
@@ -291,31 +443,7 @@ begin
"""))
end
- Base.get(select::MultiCheckBox) = Bonds.initial_value(select)
- Bonds.initial_value(select::MultiCheckBox{BT,DT}) where {BT,DT} =
- ismissing(select.default) ? BT[] : select.default
- Bonds.possible_values(select::MultiCheckBox) =
- subarrays((string(i) for i in 1:length(select.options)))
-
- function Bonds.transform_value(select::MultiCheckBox{BT,DT}, val_from_js) where {BT,DT}
- # val_from_js will be a vector of Strings, but let's allow Integers as well, there's no harm in that
- @assert val_from_js isa Vector
-
- val_nums = (
- v isa Integer ? v : tryparse(Int64, v)
- for v in val_from_js
- )
-
- BT[select.options[v].first for v in val_nums]
- end
-
- function Bonds.validate_value(select::MultiCheckBox, val)
- val isa Vector && all(val_from_js) do v
- val_num = v isa Integer ? v : tryparse(Int64, v)
- 1 ≤ val_num ≤ length(select.options)
- end
- end
- result
+ result
end
# ╔═╡ 8bfaf4c8-557d-433e-a228-aac493746efc
@@ -384,6 +512,24 @@ MultiCheckBox(["🐰 &&\\a \$\$", "🐱" , "🐵", "🐘", "🦝", "🐿️" , "
snacks
╠═╡ =#
+# ╔═╡ c8bf9a17-14ce-4802-b578-81eb10bdd7be
+bms = @bind ms1 MultiSelect(["a" => "default", teststr => teststr])
+
+# ╔═╡ 9b8efd9f-8c24-4cf0-97a1-87fb8f4dadd2
+bms
+
+# ╔═╡ 9b888e03-242f-4a41-bb25-c62fa7daf12a
+ms1
+
+# ╔═╡ 1f8f2492-8624-496d-9aba-a3d5e1d1accb
+@bind fs MultiSelect([sin, cos, tan])
+
+# ╔═╡ ad25b07b-fa5b-41eb-a928-6012e2f2c0ec
+fs
+
+# ╔═╡ 5d14aaa7-587e-4c39-8e54-2c46a1581a11
+[f(0.5) for f in fs]
+
# ╔═╡ Cell order:
# ╟─a8c1e0d2-3604-4e1d-a87c-c8f5b86b79ed
# ╠═8bfaf4c8-557d-433e-a228-aac493746efc
@@ -398,9 +544,26 @@ snacks
# ╠═60183ad1-4919-4402-83fb-d53b86dda0a6
# ╠═abe4c3e0-6e1e-4e26-a4fa-bd60f31c1a4c
# ╠═ad6dcdec-2fc9-45d2-8828-62ac857b4afa
+# ╟─ba2cf480-ff5e-43a3-8cba-5a4754b776f5
+# ╠═775c49e9-bb4d-42ca-8ed0-d259d0d18725
+# ╠═cd217cac-658a-44b7-8c88-348ae1af9b64
+# ╠═c8bf9a17-14ce-4802-b578-81eb10bdd7be
+# ╠═9b8efd9f-8c24-4cf0-97a1-87fb8f4dadd2
+# ╠═9b888e03-242f-4a41-bb25-c62fa7daf12a
+# ╠═112a576c-66fa-4336-a538-3843b8306587
+# ╠═11fbe522-4596-418c-b1d4-076a83a8408b
+# ╠═1f8f2492-8624-496d-9aba-a3d5e1d1accb
+# ╠═ad25b07b-fa5b-41eb-a928-6012e2f2c0ec
+# ╠═5d14aaa7-587e-4c39-8e54-2c46a1581a11
# ╟─c8350f43-0d30-45d0-873b-ff56c5801ac1
# ╠═499ca710-1a50-4aa1-87d8-d213416e8e30
+# ╠═693f0bc4-2076-44e4-84f4-5ef1f3f43dd7
# ╠═631c14bf-e2d3-4a24-8ddc-095a3dab80ef
# ╠═b65c67ec-b79f-4f0e-85e6-78ff22b279d4
+# ╟─a666d4df-dbbb-4837-b850-363caa576fe9
+# ╟─c38de38d-e900-4309-a9f6-1392af2f245b
+# ╟─2c634e7f-428f-4655-951b-2a8e4efce548
+# ╠═d7d9f4f3-4bac-4431-b3af-0af651a483b7
+# ╠═9cd42452-149e-4080-972f-a31f7023f67d
# ╠═430e2c1a-832f-11eb-024a-13e3989fd7c2
-# ╠═c38de38d-e900-4309-a9f6-1392af2f245b
+# ╟─41515a2f-81d7-4a47-8139-e0e4096c8cd4
diff --git a/test/Project.toml b/test/Project.toml
new file mode 100644
index 00000000..29c261cb
--- /dev/null
+++ b/test/Project.toml
@@ -0,0 +1,8 @@
+[deps]
+AbstractPlutoDingetjes = "6e696c72-6542-2067-7265-42206c756150"
+ColorTypes = "3da002f7-5984-5a60-b8a6-cbb66c0b333f"
+Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
+HypertextLiteral = "ac1192a8-f4b3-4bfe-ba22-af5b92cd3ab2"
+Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
+StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
+Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
diff --git a/test/runtests.jl b/test/runtests.jl
index d7481182..6a876feb 100644
--- a/test/runtests.jl
+++ b/test/runtests.jl
@@ -1,10 +1,12 @@
using PlutoUI
using Test
import AbstractPlutoDingetjes
+import AbstractPlutoDingetjes: Bonds
using HypertextLiteral
import ColorTypes: RGB, N0f8, Colorant
import Logging
using Dates
+import StatsBase: countmap
# has to be outside of the begin block for julia 1.0 compat
struct Uhm end
@@ -120,6 +122,12 @@ function default(x)
end
transform(el, x) = AbstractPlutoDingetjes.Bonds.transform_value(el, x)
+struct CustomVector{T} <: AbstractVector{T}
+ v::Vector{T}
+end
+Base.size(c::CustomVector) = size(c.v)
+Base.getindex(c::CustomVector, i) = c.v[i]
+
@testset "Public API" begin
el = Button()
el = Button("asdf")
@@ -183,14 +191,37 @@ transform(el, x) = AbstractPlutoDingetjes.Bonds.transform_value(el, x)
el = FilePicker()
@test default(el) === nothing
+ @testset "MultiWidgets: $f" for f in [MultiSelect, MultiCheckBox]
+ bound_values(v) = v
+ bound_values(v::AbstractVector{<:Pair}) = first.(v)
+
+ for v in (["asdf"], ["asdf", "x"], ["asdf", "x", "x"], [1:5; 1:5], ["sin" => "asdf"], (1:5 .=> 'a':'e'))
+ bv = bound_values(v)
+
+ el = f(v)
+ @test default(el) == []
+ @test default(el) isa Vector{eltype(bv)}
+ for js_x in Bonds.possible_values(el)
+ @test Bonds.validate_value(el, js_x)
+ x = Bonds.transform_value(el, js_x)
+ @test eltype(x) == eltype(bv)
+ # `x` should be a subset of `bv` in the multiset sense
+ counts_x = countmap(x)
+ counts_bv = countmap(bv)
+ @test all(counts_x[k] ≤ counts_bv[k] for k in keys(counts_x))
+ end
+ @test !Bonds.validate_value(el, ["0"])
+ @test !Bonds.validate_value(el, [string(lastindex(v)+1)])
+
+ # `default(el)` should have the contents specified in the `default` kwarg,
+ # but its type and eltype should not depend on the type of that vector
+ for def in (bv, eltype(bv)[]), g in (identity, Vector{Any}, CustomVector, CustomVector ∘ Vector{Any})
+ el = f(v; default = g(def))
+ @test default(el) == def
+ @test default(el) isa Vector{eltype(bv)}
+ end
+ end
- @testset "MultiSelect" for f in [MultiSelect, MultiCheckBox]
- el = f(["asdf", "x"])
- @test default(el) == []
- el = f(["asdf"])
- @test default(el) == []
- el = f(["sin" => "asdf"])
- @test default(el) == []
el = f(["sin" => "asdf"]; default = ["sin"])
@test default(el) == ["sin"]
@@ -203,19 +234,19 @@ transform(el, x) = AbstractPlutoDingetjes.Bonds.transform_value(el, x)
])
@test default(el) |> isempty
@test default(el) isa Vector{Function}
- end
-
- el = MultiCheckBox(
- ["🐱" => "🐝", "🐵" => "🦝", "🐱" => "🐿️"];
- default=["🐱", "🐱"]
- )
- @test default(el) == ["🐱", "🐱"]
- el = MultiCheckBox(
- ["🐱" => "🐝", "🐵" => "🦝", "🐱" => "🐿️"];
- default=["🐱"]
- )
- @test default(el) == ["🐱"]
+ # Some cases of repeated bound values
+ el = f(
+ ["🐱" => "🐝", "🐵" => "🦝", "🐱" => "🐿️"];
+ default=["🐱", "🐱"]
+ )
+ @test default(el) == ["🐱", "🐱"]
+ el = f(
+ ["🐱" => "🐝", "🐵" => "🦝", "🐱" => "🐿️"];
+ default=["🐱"]
+ )
+ @test default(el) == ["🐱"]
+ end
el = Select(["asdf", "x"])
@test default(el) == "asdf"