diff --git a/chart_option.go b/chart_option.go
index db2f57b..94574a1 100644
--- a/chart_option.go
+++ b/chart_option.go
@@ -69,9 +69,9 @@ type ChartOption struct {
// OptionFunc option function
type OptionFunc func(opt *ChartOption)
-// PNGTypeOption set png type of chart's output
-func PNGTypeOption() OptionFunc {
- return TypeOptionFunc(ChartOutputPNG)
+// SVGTypeOption set svg type of chart's output
+func SVGTypeOption() OptionFunc {
+ return TypeOptionFunc(ChartOutputSVG)
}
// TypeOptionFunc set type of chart's output
diff --git a/chart_option_test.go b/chart_option_test.go
new file mode 100644
index 0000000..c77bb4f
--- /dev/null
+++ b/chart_option_test.go
@@ -0,0 +1,451 @@
+// MIT License
+
+// Copyright (c) 2022 Tree Xie
+
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package charts
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/wcharczuk/go-chart/v2/drawing"
+)
+
+func TestChartOption(t *testing.T) {
+ assert := assert.New(t)
+
+ fns := []OptionFunc{
+ SVGTypeOption(),
+ FontFamilyOptionFunc("fontFamily"),
+ ThemeOptionFunc("theme"),
+ TitleTextOptionFunc("title"),
+ LegendLabelsOptionFunc([]string{
+ "label",
+ }),
+ XAxisDataOptionFunc([]string{
+ "xaxis",
+ }),
+ YAxisDataOptionFunc([]string{
+ "yaxis",
+ }),
+ WidthOptionFunc(800),
+ HeightOptionFunc(600),
+ PaddingOptionFunc(Box{
+ Left: 10,
+ Top: 10,
+ Right: 10,
+ Bottom: 10,
+ }),
+ BackgroundColorOptionFunc(drawing.ColorBlack),
+ }
+ opt := ChartOption{}
+ for _, fn := range fns {
+ fn(&opt)
+ }
+ assert.Equal(ChartOption{
+ Type: ChartOutputSVG,
+ FontFamily: "fontFamily",
+ Theme: "theme",
+ Title: TitleOption{
+ Text: "title",
+ },
+ Legend: LegendOption{
+ Data: []string{
+ "label",
+ },
+ },
+ XAxis: XAxisOption{
+ Data: []string{
+ "xaxis",
+ },
+ },
+ YAxisOptions: []YAxisOption{
+ {
+ Data: []string{
+ "yaxis",
+ },
+ },
+ },
+ Width: 800,
+ Height: 600,
+ Padding: Box{
+ Left: 10,
+ Top: 10,
+ Right: 10,
+ Bottom: 10,
+ },
+ BackgroundColor: drawing.ColorBlack,
+ }, opt)
+}
+
+func TestChartOptionPieSeriesShowLabel(t *testing.T) {
+ assert := assert.New(t)
+
+ opt := ChartOption{
+ SeriesList: NewPieSeriesList([]float64{
+ 1,
+ 2,
+ }),
+ }
+ PieSeriesShowLabel()(&opt)
+ assert.True(opt.SeriesList[0].Label.Show)
+}
+
+func TestChartOptionMarkLine(t *testing.T) {
+ assert := assert.New(t)
+ opt := ChartOption{
+ SeriesList: NewSeriesListDataFromValues([][]float64{
+ {1, 2},
+ }),
+ }
+ MarkLineOptionFunc(0, "min", "max")(&opt)
+ assert.Equal(NewMarkLine("min", "max"), opt.SeriesList[0].MarkLine)
+}
+
+func TestChartOptionMarkPoint(t *testing.T) {
+ assert := assert.New(t)
+ opt := ChartOption{
+ SeriesList: NewSeriesListDataFromValues([][]float64{
+ {1, 2},
+ }),
+ }
+ MarkPointOptionFunc(0, "min", "max")(&opt)
+ assert.Equal(NewMarkPoint("min", "max"), opt.SeriesList[0].MarkPoint)
+}
+
+func TestLineRender(t *testing.T) {
+ assert := assert.New(t)
+ values := [][]float64{
+ {
+ 120,
+ 132,
+ 101,
+ 134,
+ 90,
+ 230,
+ 210,
+ },
+ {
+ 220,
+ 182,
+ 191,
+ 234,
+ 290,
+ 330,
+ 310,
+ },
+ {
+ 150,
+ 232,
+ 201,
+ 154,
+ 190,
+ 330,
+ 410,
+ },
+ {
+ 320,
+ 332,
+ 301,
+ 334,
+ 390,
+ 330,
+ 320,
+ },
+ {
+ 820,
+ 932,
+ 901,
+ 934,
+ 1290,
+ 1330,
+ 1320,
+ },
+ }
+ p, err := LineRender(
+ values,
+ SVGTypeOption(),
+ TitleTextOptionFunc("Line"),
+ XAxisDataOptionFunc([]string{
+ "Mon",
+ "Tue",
+ "Wed",
+ "Thu",
+ "Fri",
+ "Sat",
+ "Sun",
+ }),
+ LegendLabelsOptionFunc([]string{
+ "Email",
+ "Union Ads",
+ "Video Ads",
+ "Direct",
+ "Search Engine",
+ }, PositionCenter),
+ )
+ assert.Nil(err)
+ data, err := p.Bytes()
+ assert.Nil(err)
+ assert.Equal("", string(data))
+}
+
+func TestBarRender(t *testing.T) {
+ assert := assert.New(t)
+ values := [][]float64{
+ {
+ 2.0,
+ 4.9,
+ 7.0,
+ 23.2,
+ 25.6,
+ 76.7,
+ 135.6,
+ 162.2,
+ 32.6,
+ 20.0,
+ 6.4,
+ 3.3,
+ },
+ {
+ 2.6,
+ 5.9,
+ 9.0,
+ 26.4,
+ 28.7,
+ 70.7,
+ 175.6,
+ 182.2,
+ 48.7,
+ 18.8,
+ 6.0,
+ 2.3,
+ },
+ }
+ p, err := BarRender(
+ values,
+ SVGTypeOption(),
+ XAxisDataOptionFunc([]string{
+ "Jan",
+ "Feb",
+ "Mar",
+ "Apr",
+ "May",
+ "Jun",
+ "Jul",
+ "Aug",
+ "Sep",
+ "Oct",
+ "Nov",
+ "Dec",
+ }),
+ LegendLabelsOptionFunc([]string{
+ "Rainfall",
+ "Evaporation",
+ }, PositionRight),
+ MarkLineOptionFunc(0, SeriesMarkDataTypeAverage),
+ MarkPointOptionFunc(0, SeriesMarkDataTypeMax,
+ SeriesMarkDataTypeMin),
+ // custom option func
+ func(opt *ChartOption) {
+ opt.SeriesList[1].MarkPoint = NewMarkPoint(
+ SeriesMarkDataTypeMax,
+ SeriesMarkDataTypeMin,
+ )
+ opt.SeriesList[1].MarkLine = NewMarkLine(
+ SeriesMarkDataTypeAverage,
+ )
+ },
+ )
+ assert.Nil(err)
+ data, err := p.Bytes()
+ assert.Nil(err)
+ assert.Equal("", string(data))
+}
+
+func TestHorizontalBarRender(t *testing.T) {
+ assert := assert.New(t)
+ values := [][]float64{
+ {
+ 18203,
+ 23489,
+ 29034,
+ 104970,
+ 131744,
+ 630230,
+ },
+ {
+ 19325,
+ 23438,
+ 31000,
+ 121594,
+ 134141,
+ 681807,
+ },
+ }
+ p, err := HorizontalBarRender(
+ values,
+ SVGTypeOption(),
+ TitleTextOptionFunc("World Population"),
+ PaddingOptionFunc(Box{
+ Top: 20,
+ Right: 40,
+ Bottom: 20,
+ Left: 20,
+ }),
+ LegendLabelsOptionFunc([]string{
+ "2011",
+ "2012",
+ }),
+ YAxisDataOptionFunc([]string{
+ "Brazil",
+ "Indonesia",
+ "USA",
+ "India",
+ "China",
+ "World",
+ }),
+ )
+ assert.Nil(err)
+ data, err := p.Bytes()
+ assert.Nil(err)
+ assert.Equal("", string(data))
+}
+
+func TestPieRender(t *testing.T) {
+ assert := assert.New(t)
+ values := []float64{
+ 1048,
+ 735,
+ 580,
+ 484,
+ 300,
+ }
+ p, err := PieRender(
+ values,
+ SVGTypeOption(),
+ TitleOptionFunc(TitleOption{
+ Text: "Rainfall vs Evaporation",
+ Subtext: "Fake Data",
+ Left: PositionCenter,
+ }),
+ PaddingOptionFunc(Box{
+ Top: 20,
+ Right: 20,
+ Bottom: 20,
+ Left: 20,
+ }),
+ LegendOptionFunc(LegendOption{
+ Orient: OrientVertical,
+ Data: []string{
+ "Search Engine",
+ "Direct",
+ "Email",
+ "Union Ads",
+ "Video Ads",
+ },
+ Left: PositionLeft,
+ }),
+ PieSeriesShowLabel(),
+ )
+ assert.Nil(err)
+ data, err := p.Bytes()
+ assert.Nil(err)
+ assert.Equal("", string(data))
+}
+
+func TestRadarRender(t *testing.T) {
+ assert := assert.New(t)
+
+ values := [][]float64{
+ {
+ 4200,
+ 3000,
+ 20000,
+ 35000,
+ 50000,
+ 18000,
+ },
+ {
+ 5000,
+ 14000,
+ 28000,
+ 26000,
+ 42000,
+ 21000,
+ },
+ }
+ p, err := RadarRender(
+ values,
+ SVGTypeOption(),
+ TitleTextOptionFunc("Basic Radar Chart"),
+ LegendLabelsOptionFunc([]string{
+ "Allocated Budget",
+ "Actual Spending",
+ }),
+ RadarIndicatorOptionFunc([]string{
+ "Sales",
+ "Administration",
+ "Information Technology",
+ "Customer Support",
+ "Development",
+ "Marketing",
+ }, []float64{
+ 6500,
+ 16000,
+ 30000,
+ 38000,
+ 52000,
+ 25000,
+ }),
+ )
+ assert.Nil(err)
+ data, err := p.Bytes()
+ assert.Nil(err)
+ assert.Equal("", string(data))
+}
+
+func TestFunnelRender(t *testing.T) {
+ assert := assert.New(t)
+
+ values := []float64{
+ 100,
+ 80,
+ 60,
+ 40,
+ 20,
+ }
+ p, err := FunnelRender(
+ values,
+ SVGTypeOption(),
+ TitleTextOptionFunc("Funnel"),
+ LegendLabelsOptionFunc([]string{
+ "Show",
+ "Click",
+ "Visit",
+ "Inquiry",
+ "Order",
+ }),
+ )
+ assert.Nil(err)
+ data, err := p.Bytes()
+ assert.Nil(err)
+ assert.Equal("", string(data))
+}
diff --git a/echarts.go b/echarts.go
index d2602b3..fbe9a36 100644
--- a/echarts.go
+++ b/echarts.go
@@ -60,9 +60,9 @@ type EChartStyle struct {
Color string `json:"color"`
}
-func (es *EChartStyle) ToStyle() chart.Style {
+func (es *EChartStyle) ToStyle() Style {
color := parseColor(es.Color)
- return chart.Style{
+ return Style{
FillColor: color,
FontColor: color,
StrokeColor: color,
diff --git a/echarts_test.go b/echarts_test.go
index 1ed14d3..9c31286 100644
--- a/echarts_test.go
+++ b/echarts_test.go
@@ -27,6 +27,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
+ "github.com/wcharczuk/go-chart/v2/drawing"
)
func TestConvertToArray(t *testing.T) {
@@ -59,6 +60,7 @@ func TestEChartsSeriesDataValue(t *testing.T) {
2,
},
}, es)
+ assert.Equal(NewEChartsSeriesDataValue(1, 2), es)
assert.Equal(1.0, es.First())
}
@@ -109,47 +111,380 @@ func TestEChartsXAxis(t *testing.T) {
}, ex)
}
+func TestEChartStyle(t *testing.T) {
+ assert := assert.New(t)
+
+ es := EChartStyle{
+ Color: "#999",
+ }
+ color := drawing.Color{
+ R: 153,
+ G: 153,
+ B: 153,
+ A: 255,
+ }
+ assert.Equal(Style{
+ FillColor: color,
+ FontColor: color,
+ StrokeColor: color,
+ }, es.ToStyle())
+}
+
+func TestEChartsPadding(t *testing.T) {
+ assert := assert.New(t)
+
+ eb := EChartsPadding{}
+
+ err := eb.UnmarshalJSON([]byte(`1`))
+ assert.Nil(err)
+ assert.Equal(Box{
+ Left: 1,
+ Top: 1,
+ Right: 1,
+ Bottom: 1,
+ }, eb.Box)
+
+ err = eb.UnmarshalJSON([]byte(`[2, 3]`))
+ assert.Nil(err)
+ assert.Equal(Box{
+ Left: 3,
+ Top: 2,
+ Right: 3,
+ Bottom: 2,
+ }, eb.Box)
+
+ err = eb.UnmarshalJSON([]byte(`[4, 5, 6]`))
+ assert.Nil(err)
+ assert.Equal(Box{
+ Left: 5,
+ Top: 4,
+ Right: 5,
+ Bottom: 6,
+ }, eb.Box)
+
+ err = eb.UnmarshalJSON([]byte(`[4, 5, 6, 7]`))
+ assert.Nil(err)
+ assert.Equal(Box{
+ Left: 7,
+ Top: 4,
+ Right: 5,
+ Bottom: 6,
+ }, eb.Box)
+}
+
+func TestEChartsMarkPoint(t *testing.T) {
+ assert := assert.New(t)
+
+ emp := EChartsMarkPoint{
+ SymbolSize: 30,
+ Data: []EChartsMarkData{
+ {
+ Type: "test",
+ },
+ },
+ }
+ assert.Equal(SeriesMarkPoint{
+ SymbolSize: 30,
+ Data: []SeriesMarkData{
+ {
+ Type: "test",
+ },
+ },
+ }, emp.ToSeriesMarkPoint())
+}
+
+func TestEChartsMarkLine(t *testing.T) {
+ assert := assert.New(t)
+
+ eml := EChartsMarkLine{
+ Data: []EChartsMarkData{
+ {
+ Type: "min",
+ },
+ {
+ Type: "max",
+ },
+ },
+ }
+ assert.Equal(SeriesMarkLine{
+ Data: []SeriesMarkData{
+ {
+ Type: "min",
+ },
+ {
+ Type: "max",
+ },
+ },
+ }, eml.ToSeriesMarkLine())
+}
+
func TestEChartsOption(t *testing.T) {
assert := assert.New(t)
- opt := EChartsOption{}
- err := json.Unmarshal([]byte(`{
+ tests := []struct {
+ option string
+ }{
+ {
+ option: `{
+ "xAxis": {
+ "type": "category",
+ "data": [
+ "Mon",
+ "Tue",
+ "Wed",
+ "Thu",
+ "Fri",
+ "Sat",
+ "Sun"
+ ]
+ },
+ "yAxis": {
+ "type": "value"
+ },
+ "series": [
+ {
+ "data": [
+ 120,
+ {
+ "value": 200,
+ "itemStyle": {
+ "color": "#a90000"
+ }
+ },
+ 150,
+ 80,
+ 70,
+ 110,
+ 130
+ ],
+ "type": "bar"
+ }
+ ]
+ }`,
+ },
+ {
+ option: `{
+ "title": {
+ "text": "Referer of a Website",
+ "subtext": "Fake Data",
+ "left": "center"
+ },
+ "tooltip": {
+ "trigger": "item"
+ },
+ "legend": {
+ "orient": "vertical",
+ "left": "left"
+ },
+ "series": [
+ {
+ "name": "Access From",
+ "type": "pie",
+ "radius": "50%",
+ "data": [
+ {
+ "value": 1048,
+ "name": "Search Engine"
+ },
+ {
+ "value": 735,
+ "name": "Direct"
+ },
+ {
+ "value": 580,
+ "name": "Email"
+ },
+ {
+ "value": 484,
+ "name": "Union Ads"
+ },
+ {
+ "value": 300,
+ "name": "Video Ads"
+ }
+ ],
+ "emphasis": {
+ "itemStyle": {
+ "shadowBlur": 10,
+ "shadowOffsetX": 0,
+ "shadowColor": "rgba(0, 0, 0, 0.5)"
+ }
+ }
+ }
+ ]
+ }`,
+ },
+ {
+ option: `{
+ "title": {
+ "text": "Rainfall vs Evaporation",
+ "subtext": "Fake Data"
+ },
+ "tooltip": {
+ "trigger": "axis"
+ },
+ "legend": {
+ "data": [
+ "Rainfall",
+ "Evaporation"
+ ]
+ },
+ "toolbox": {
+ "show": true,
+ "feature": {
+ "dataView": {
+ "show": true,
+ "readOnly": false
+ },
+ "magicType": {
+ "show": true,
+ "type": [
+ "line",
+ "bar"
+ ]
+ },
+ "restore": {
+ "show": true
+ },
+ "saveAsImage": {
+ "show": true
+ }
+ }
+ },
+ "calculable": true,
+ "xAxis": [
+ {
+ "type": "category",
+ "data": [
+ "Jan",
+ "Feb",
+ "Mar",
+ "Apr",
+ "May",
+ "Jun",
+ "Jul",
+ "Aug",
+ "Sep",
+ "Oct",
+ "Nov",
+ "Dec"
+ ]
+ }
+ ],
+ "yAxis": [
+ {
+ "type": "value"
+ }
+ ],
+ "series": [
+ {
+ "name": "Rainfall",
+ "type": "bar",
+ "data": [
+ 2,
+ 4.9,
+ 7,
+ 23.2,
+ 25.6,
+ 76.7,
+ 135.6,
+ 162.2,
+ 32.6,
+ 20,
+ 6.4,
+ 3.3
+ ],
+ "markPoint": {
+ "data": [
+ {
+ "type": "max",
+ "name": "Max"
+ },
+ {
+ "type": "min",
+ "name": "Min"
+ }
+ ]
+ },
+ "markLine": {
+ "data": [
+ {
+ "type": "average",
+ "name": "Avg"
+ }
+ ]
+ }
+ },
+ {
+ "name": "Evaporation",
+ "type": "bar",
+ "data": [
+ 2.6,
+ 5.9,
+ 9,
+ 26.4,
+ 28.7,
+ 70.7,
+ 175.6,
+ 182.2,
+ 48.7,
+ 18.8,
+ 6,
+ 2.3
+ ],
+ "markPoint": {
+ "data": [
+ {
+ "name": "Max",
+ "value": 182.2,
+ "xAxis": 7,
+ "yAxis": 183
+ },
+ {
+ "name": "Min",
+ "value": 2.3,
+ "xAxis": 11,
+ "yAxis": 3
+ }
+ ]
+ },
+ "markLine": {
+ "data": [
+ {
+ "type": "average",
+ "name": "Avg"
+ }
+ ]
+ }
+ }
+ ]
+ }`,
+ },
+ }
+ for _, tt := range tests {
+ opt := EChartsOption{}
+ err := json.Unmarshal([]byte(tt.option), &opt)
+ assert.Nil(err)
+ assert.NotEmpty(opt.Series)
+ assert.NotEmpty(opt.ToOption().SeriesList)
+ }
+}
+
+func TestRenderEChartsToSVG(t *testing.T) {
+ assert := assert.New(t)
+
+ data, err := RenderEChartsToSVG(`{
"title": {
"text": "Rainfall vs Evaporation",
"subtext": "Fake Data"
},
- "tooltip": {
- "trigger": "axis"
- },
"legend": {
"data": [
"Rainfall",
"Evaporation"
]
},
- "toolbox": {
- "show": true,
- "feature": {
- "dataView": {
- "show": true,
- "readOnly": false
- },
- "magicType": {
- "show": true,
- "type": [
- "line",
- "bar"
- ]
- },
- "restore": {
- "show": true
- },
- "saveAsImage": {
- "show": true
- }
- }
- },
- "calculable": true,
+ "padding": [10, 30, 10, 10],
"xAxis": [
{
"type": "category",
@@ -169,11 +504,6 @@ func TestEChartsOption(t *testing.T) {
]
}
],
- "yAxis": [
- {
- "type": "value"
- }
- ],
"series": [
{
"name": "Rainfall",
@@ -195,20 +525,17 @@ func TestEChartsOption(t *testing.T) {
"markPoint": {
"data": [
{
- "type": "max",
- "name": "Max"
+ "type": "max"
},
{
- "type": "min",
- "name": "Min"
+ "type": "min"
}
]
},
"markLine": {
"data": [
{
- "type": "average",
- "name": "Avg"
+ "type": "average"
}
]
}
@@ -233,31 +560,23 @@ func TestEChartsOption(t *testing.T) {
"markPoint": {
"data": [
{
- "name": "Max",
- "value": 182.2,
- "xAxis": 7,
- "yAxis": 183
+ "type": "max"
},
{
- "name": "Min",
- "value": 2.3,
- "xAxis": 11,
- "yAxis": 3
+ "type": "min"
}
]
},
"markLine": {
"data": [
{
- "type": "average",
- "name": "Avg"
+ "type": "average"
}
]
}
}
]
- }`), &opt)
-
+ }`)
assert.Nil(err)
- assert.NotEmpty(opt.Series)
+ assert.Equal("", string(data))
}
diff --git a/legend_test.go b/legend_test.go
index 9078006..526f178 100644
--- a/legend_test.go
+++ b/legend_test.go
@@ -23,7 +23,6 @@
package charts
import (
- "fmt"
"testing"
"github.com/stretchr/testify/assert"
@@ -98,7 +97,6 @@ func TestNewLegend(t *testing.T) {
assert.Nil(err)
data, err := tt.render(p)
assert.Nil(err)
- fmt.Println(string(data))
assert.Equal(tt.result, string(data))
}
}
diff --git a/radar_chart_test.go b/radar_chart_test.go
index baf616d..79fd9ac 100644
--- a/radar_chart_test.go
+++ b/radar_chart_test.go
@@ -23,7 +23,6 @@
package charts
import (
- "fmt"
"testing"
"github.com/stretchr/testify/assert"
@@ -103,7 +102,6 @@ func TestRadarChart(t *testing.T) {
Bottom: 20,
})))
assert.Nil(err)
- fmt.Println(string(data))
assert.Equal(tt.result, string(data))
}
}