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"