diff --git a/Project.toml b/Project.toml index a2a736e..122b939 100644 --- a/Project.toml +++ b/Project.toml @@ -12,12 +12,20 @@ Stipple = "4acbeb90-81a0-11ea-1966-bdaff8155998" [compat] DataFrames = "1" Genie = "5" +PlotlyBase = "0.8.19" Requires = "1" Stipple = "0.23, 0.24, 0.25, 0.26, 0.27" julia = "1.6" [extras] +PlotlyBase = "a03496cd-edff-5a9b-9e67-9cda94a718b5" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +[extensions] +StipplePlotlyPlotlyBaseExt = "PlotlyBase" + [targets] -test = ["Test"] +test = ["Test", "PlotlyBase"] + +[weakdeps] +PlotlyBase = "a03496cd-edff-5a9b-9e67-9cda94a718b5" diff --git a/ext/StipplePlotlyPlotlyBaseExt.jl b/ext/StipplePlotlyPlotlyBaseExt.jl new file mode 100644 index 0000000..53043e0 --- /dev/null +++ b/ext/StipplePlotlyPlotlyBaseExt.jl @@ -0,0 +1,35 @@ +module StipplePlotlyPlotlyBaseExt + +using StipplePlotly +using StipplePlotly.Stipple +using StipplePlotly.Charts + +isdefined(Base, :get_extension) ? (using PlotlyBase) : (using ..PlotlyBase) + +Base.print(io::IO, a::Union{PlotlyBase.PlotConfig}) = print(io, Stipple.json(a)) +PlotlyBase.JSON.Writer.lower(json::Stipple.JSONText) = json.s + +function Base.Dict(p::PlotlyBase.Plot) + Dict( + :data => p.data, + :layout => p.layout, + :frames => p.frames, + :config => p.config + ) +end + +function PlotlyBase.Plot(d::AbstractDict) + sd = symbol_dict(d) + data = haskey(sd, :data) && ! isempty(sd[:data]) ? PlotlyBase.GenericTrace.(sd[:data]) : PlotlyBase.GenericTrace[] + layout = haskey(sd, :layout) ? PlotlyBase.Layout(sd[:layout]) : PlotlyBase.Layout() + frames = haskey(sd, :frames) && ! isempty(sd[:frames]) ? PlotlyBase.PlotlyFrame.(sd[:frames]) : PlotlyBase.PlotlyFrame[] + config = haskey(sd, :config) ? PlotlyBase.PlotConfig(; sd[:config]...) : PlotlyBase.PlotConfig() + + PlotlyBase.Plot(data, layout, frames; config) +end + +function stipple_parse(::Type{PlotlyBase.Plot}, d::AbstractDict) + PlotlyBase.Plot(d) +end + +end \ No newline at end of file diff --git a/src/Charts.jl b/src/Charts.jl index 1f2c04b..60ed9bd 100644 --- a/src/Charts.jl +++ b/src/Charts.jl @@ -17,12 +17,11 @@ using .Layouts using .Layouts:optionals! import Genie.Renderer.Html: HTMLString, normal_element, register_normal_element -using Requires export PlotLayout, PlotData, PlotAnnotation, Trace, plot, ErrorBar, Font, ColorBar, watchplot, watchplots export PlotLayoutGrid, PlotLayoutAxis export PlotConfig, PlotLayoutTitle, PlotLayoutLegend, PlotlyLine, PlotDataMarker -export PlotlyEvents, PlotWithEvents, PBPlotWithEvents, PlotWithEventsReadOnly, PBPlotWithEventsReadOnly +export PlotlyEvents, PlotWithEvents, PlotWithEventsReadOnly export plotdata const PLOT_TYPE_LINE = "scatter" @@ -67,57 +66,20 @@ register_normal_element("plotly", context = @__MODULE__) function __init__() DEFAULT_CONFIG_TYPE[] = Charts.PlotConfig +end - @require PlotlyBase = "a03496cd-edff-5a9b-9e67-9cda94a718b5" begin - DEFAULT_CONFIG_TYPE[] = PlotlyBase.PlotConfig - - Base.print(io::IO, a::Union{PlotlyBase.PlotConfig}) = print(io, Stipple.json(a)) - StructTypes.StructType(::Type{<:PlotlyBase.HasFields}) = JSON3.DictType() - StructTypes.StructType(::Type{PlotlyBase.PlotConfig}) = JSON3.DictType() - - function Base.Dict(p::PlotlyBase.Plot) - Dict( - :data => p.data, - :layout => p.layout, - :frames => p.frames, - :config => p.config - ) - end - - Base.@kwdef struct PBPlotWithEvents - var""::R{PlotlyBase.Plot} = PlotlyBase.Plot() - _selected::R{PlotlyEvent} = PlotlyEvent() - _hover::R{PlotlyEvent} = PlotlyEvent() - _click::R{PlotlyEvent} = PlotlyEvent() - _relayout::R{PlotlyEvent} = PlotlyEvent() - end - - Base.@kwdef struct PBPlotWithEventsReadOnly - var""::R{PlotlyBase.Plot} = PlotlyBase.Plot(), READONLY - _selected::R{PlotlyEvent} = PlotlyEvent() - _hover::R{PlotlyEvent} = PlotlyEvent() - _click::R{PlotlyEvent} = PlotlyEvent() - _relayout::R{PlotlyEvent} = PlotlyEvent() - end - - function PlotlyBase.Plot(d::AbstractDict) - sd = symbol_dict(d) - data = haskey(sd, :data) && ! isempty(sd[:data]) ? PlotlyBase.GenericTrace.(sd[:data]) : PlotlyBase.GenericTrace[] - layout = haskey(sd, :layout) ? PlotlyBase.Layout(sd[:layout]) : PlotlyBase.Layout() - frames = haskey(sd, :frames) && ! isempty(sd[:frames]) ? PlotlyBase.PlotlyFrame.(sd[:frames]) : PlotlyBase.PlotlyFrame[] - config = haskey(sd, :config) ? PlotlyBase.PlotConfig(; sd[:config]...) : PlotlyBase.PlotConfig() - - PlotlyBase.Plot(data, layout, frames; config) - end - - function stipple_parse(::Type{PlotlyBase.Plot}, d::AbstractDict) - PlotlyBase.Plot(d) +function default_config_type() + if DEFAULT_CONFIG_TYPE[] == Charts.PlotConfig + pkgid = Base.identify_package("PlotlyBase") + if haskey(Base.loaded_modules, pkgid) + DEFAULT_CONFIG_TYPE[] = Base.loaded_modules[pkgid].PlotConfig end end + DEFAULT_CONFIG_TYPE[] end """ - function plotly(p::Symbol; layout = Symbol(p, ".layout"), config = Symbol(p, ".config"), configtype = DEFAULT_CONFIG_TYPE[], kwargs...) + function plotly(p::Symbol; layout = Symbol(p, ".layout"), config = Symbol(p, ".config"), configtype = default_config_type(), kwargs...) This is a convenience function for rendering a PlotlyBase.Plot or a struct with fields data, layout and config # Example @@ -132,7 +94,7 @@ julia> plotly(:plot, config = :config) ``` """ -function plotly(p::Symbol, args...; layout = Symbol(p, ".layout"), config = Symbol(p, ".config"), configtype = DEFAULT_CONFIG_TYPE[], kwargs...) +function plotly(p::Symbol, args...; layout = Symbol(p, ".layout"), config = Symbol(p, ".config"), configtype = default_config_type(), kwargs...) plot("$p.data", args...; layout, config, configtype, kwargs...) end diff --git a/src/StipplePlotly.jl b/src/StipplePlotly.jl index 52daaa2..9bcb1ec 100644 --- a/src/StipplePlotly.jl +++ b/src/StipplePlotly.jl @@ -1,6 +1,7 @@ module StipplePlotly using Genie, Stipple, Stipple.Reexport, Stipple.ParsingTools +using Requires #===# @@ -83,6 +84,29 @@ include("Layouts.jl") function __init__() deps_routes() Stipple.deps!(@__MODULE__, deps) + + @require PlotlyBase = "a03496cd-edff-5a9b-9e67-9cda94a718b5" begin + @static if !isdefined(Base, :get_extension) + include("../ext/StipplePlotlyPlotlyBaseExt.jl") + end + + export PBPlotWithEvents, PBPlotWithEventsReadOnly + Base.@kwdef struct PBPlotWithEvents + var""::R{PlotlyBase.Plot} = PlotlyBase.Plot() + _selected::R{Charts.PlotlyEvent} = Charts.PlotlyEvent() + _hover::R{Charts.PlotlyEvent} = Charts.PlotlyEvent() + _click::R{Charts.PlotlyEvent} = Charts.PlotlyEvent() + _relayout::R{Charts.PlotlyEvent} = Charts.PlotlyEvent() + end + + Base.@kwdef struct PBPlotWithEventsReadOnly + var""::R{PlotlyBase.Plot} = PlotlyBase.Plot(), READONLY + _selected::R{Charts.PlotlyEvent} = Charts.PlotlyEvent() + _hover::R{Charts.PlotlyEvent} = Charts.PlotlyEvent() + _click::R{Charts.PlotlyEvent} = Charts.PlotlyEvent() + _relayout::R{Charts.PlotlyEvent} = Charts.PlotlyEvent() + end + end end end # module diff --git a/test/Charts.jl b/test/Charts.jl index 856b81b..5ce651b 100644 --- a/test/Charts.jl +++ b/test/Charts.jl @@ -7,7 +7,6 @@ function create_example_dataframe() end @testset "Scatter Plots" begin - @info "Test: Scatter Plots" @testset "Multiple Groups without Tooltips" begin df = create_example_dataframe() pd = plotdata(df, :X, :Y; groupfeature = :Group) @@ -17,6 +16,7 @@ end @test isnothing(pd[1].text) @test isnothing(pd[2].text) end + @testset "Multiple Groups with Tooltips" begin df = create_example_dataframe() pd = plotdata(df, :X, :Y; groupfeature = :Group, text = df.Label) @@ -34,4 +34,23 @@ end @test pd[2].text[2] == "D" end end +end + +@testset "JSONText from PlotlyBase extension" begin + + using Stipple + @testset "Stipple.JSONText" begin + @test ! @isdefined PBPlotWithEvents + using PlotlyBase, PlotlyBase.JSON + @test @isdefined PBPlotWithEvents + + sc = scatter(x = StipplePlotly.JSONText("jsontext"), more_of_this = "a") + pl = Plot(sc) + @test JSON.json(sc) == "{\"type\":\"scatter\",\"more\":{\"of\":{\"this\":\"a\"}},\"x\":\"jsontext\"}" + @test contains(JSON.json(pl), "{\"type\":\"scatter\",\"more\":{\"of\":{\"this\":\"a\"}},\"x\":\"jsontext\"}") + + @test Stipple.json(sc) == "{\"type\":\"scatter\",\"more\":{\"of\":{\"this\":\"a\"}},\"x\":\"jsontext\"}" + @test contains(Stipple.json(pl), "{\"type\":\"scatter\",\"more\":{\"of\":{\"this\":\"a\"}},\"x\":\"jsontext\"}") + end + end \ No newline at end of file diff --git a/test/runtests.jl b/test/runtests.jl index 04d6164..37fdd9b 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -2,7 +2,7 @@ using StipplePlotly using Test using DataFrames -@testset "StipplePlotly.jl" begin +@testset verbose = true "Charts" begin include("Charts.jl")