Skip to content

Commit

Permalink
feat: add support for stacking in timeseries panel
Browse files Browse the repository at this point in the history
Signed-off-by: Martin Chodur <m.chodur@seznam.cz>
  • Loading branch information
FUSAKLA authored and K-Phoen committed Feb 19, 2023
1 parent 24710ed commit 6bdfd3b
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 0 deletions.
26 changes: 26 additions & 0 deletions decoder/timeseries.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
var ErrInvalidGradientMode = fmt.Errorf("invalid gradient mode")
var ErrInvalidLineInterpolationMode = fmt.Errorf("invalid line interpolation mode")
var ErrInvalidTooltipMode = fmt.Errorf("invalid tooltip mode")
var ErrInvalidStackMode = fmt.Errorf("invalid stack mode")
var ErrInvalidAxisDisplay = fmt.Errorf("invalid axis display")
var ErrInvalidAxisScale = fmt.Errorf("invalid axis scale")

Expand Down Expand Up @@ -177,6 +178,7 @@ func (timeseriesPanel DashboardTimeSeries) target(t Target) (timeseries.Option,
type TimeSeriesVisualization struct {
GradientMode string `yaml:"gradient_mode,omitempty"`
Tooltip string `yaml:"tooltip,omitempty"`
Stack string `yaml:"stack,omitempty"`
FillOpacity *int `yaml:"fill_opacity,omitempty"`
PointSize *int `yaml:"point_size,omitempty"`
LineInterpolation string `yaml:"line_interpolation,omitempty"`
Expand Down Expand Up @@ -213,6 +215,14 @@ func (timeseriesViz *TimeSeriesVisualization) toOptions() ([]timeseries.Option,

opts = append(opts, gradient)
}
if timeseriesViz.Stack != "" {
stack, err := timeseriesViz.stackOption()
if err != nil {
return nil, err
}

opts = append(opts, stack)
}
if timeseriesViz.LineInterpolation != "" {
interpolationOpt, err := timeseriesViz.lineInterpolationOption()
if err != nil {
Expand Down Expand Up @@ -280,6 +290,22 @@ func (timeseriesViz *TimeSeriesVisualization) tooltipOption() (timeseries.Option
return timeseries.Tooltip(mode), nil
}

func (timeseriesViz *TimeSeriesVisualization) stackOption() (timeseries.Option, error) {
var mode timeseries.StackMode
switch timeseriesViz.Stack {
case "normal":
mode = timeseries.Normal
case "percent":
mode = timeseries.Percent
case "none":
mode = timeseries.Unstacked
default:
return timeseries.Stack(mode), ErrInvalidStackMode
}

return timeseries.Stack(mode), nil
}

type TimeSeriesAxis struct {
SoftMin *int `yaml:"soft_min,omitempty"`
SoftMax *int `yaml:"soft_max,omitempty"`
Expand Down
51 changes: 51 additions & 0 deletions decoder/timeseries_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,57 @@ func TestTimeSeriesTooltipRejectsInvalidValues(t *testing.T) {
req.Equal(ErrInvalidTooltipMode, err)
}

func TestTimeSeriesStackCanBeDecided(t *testing.T) {
testCases := []struct {
mode string
expectedMode timeseries.StackMode
}{
{
mode: "none",
expectedMode: timeseries.Unstacked,
},
{
mode: "normal",
expectedMode: timeseries.Normal,
},
{
mode: "percent",
expectedMode: timeseries.Percent,
},
}

for _, testCase := range testCases {
tc := testCase

t.Run(tc.mode, func(t *testing.T) {
req := require.New(t)

viz := TimeSeriesVisualization{
Stack: tc.mode,
}
opts, err := viz.toOptions()

req.NoError(err)

tsPanel, err := timeseries.New("", opts...)

req.NoError(err)
req.Equal(string(tc.expectedMode), tsPanel.Builder.TimeseriesPanel.FieldConfig.Defaults.Custom.Stacking.Mode)
})
}
}

func TestTimeSeriesStackRejectsInvalidValues(t *testing.T) {
req := require.New(t)

viz := TimeSeriesVisualization{
Stack: "invalid",
}
_, err := viz.toOptions()

req.Equal(ErrInvalidStackMode, err)
}

func TestTimeSeriesAxisSupportsDisplay(t *testing.T) {
testCases := []struct {
value string
Expand Down
21 changes: 21 additions & 0 deletions timeseries/timeseries.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,18 @@ const (
NoSeries TooltipMode = "none"
)

// StackMode configures mode of series stacking.
type StackMode string

const (
// Unstacked will not stack series
Unstacked StackMode = "none"
// Normal will stack series as absolute numbers
Normal StackMode = "normal"
// Percent will stack series as percents
Percent StackMode = "percent"
)

// LineInterpolationMode defines how Grafana interpolates series lines when drawn as lines.
type LineInterpolationMode string

Expand Down Expand Up @@ -189,6 +201,15 @@ func LineWidth(value int) Option {
}
}

// Stack defines if the series should be stacked and using which mode (default not stacked). the opacity level of the series. The lower the value, the more transparent.
func Stack(value StackMode) Option {
return func(timeseries *TimeSeries) error {
timeseries.Builder.TimeseriesPanel.FieldConfig.Defaults.Custom.Stacking.Mode = string(value)

return nil
}
}

// FillOpacity defines the opacity level of the series. The lower the value, the more transparent.
func FillOpacity(value int) Option {
return func(timeseries *TimeSeries) error {
Expand Down

0 comments on commit 6bdfd3b

Please sign in to comment.