From f05c1d07741388db0c9eef4eaf3a360cf1d09449 Mon Sep 17 00:00:00 2001 From: Tim Raymond Date: Wed, 19 Jul 2017 10:27:21 -0400 Subject: [PATCH 01/93] Add Axes to Dashboard Cells The frontend would like to store viewport information for each cell so that visualizations are zoomed to the proper extents upon rendering. This adds a property to cells called "axes" which takes the following shape: ``` { "axes" : { "y" : { "bounds" : [ 0, 2 ] }, "y2" : { "bounds" : [ 1, 3 ] } } } ``` Bounds specify the visible range for the axis, and are a 2-tuple of the form [lower, upper]. Bounds are not implicitly inclusive or exclusive--that determination is left for clients to make. Also, there are no restrictions on the naming of axes. --- bolt/internal/internal.go | 22 +++++ bolt/internal/internal.pb.go | 170 ++++++++++++++++++++--------------- bolt/internal/internal.proto | 21 +++-- chronograf.go | 22 +++-- 4 files changed, 146 insertions(+), 89 deletions(-) diff --git a/bolt/internal/internal.go b/bolt/internal/internal.go index 11f35474ed..4b1f7b5e75 100644 --- a/bolt/internal/internal.go +++ b/bolt/internal/internal.go @@ -179,6 +179,19 @@ func MarshalDashboard(d chronograf.Dashboard) ([]byte, error) { } } + axes := make(map[string]*DashboardRange, len(c.Axes)) + for a, r := range c.Axes { + // need to explicitly allocate a new array because r.Bounds is + // over-written and the resulting slices from previous iterations will + // point to later iteration's data. It is _not_ enough to simply re-slice + // r.Bounds + axis := [2]int32{} + copy(axis[:], r.Bounds[:2]) + axes[a] = &DashboardRange{ + Bounds: axis[:], + } + } + cells[i] = &DashboardCell{ ID: c.ID, X: c.X, @@ -188,6 +201,7 @@ func MarshalDashboard(d chronograf.Dashboard) ([]byte, error) { Name: c.Name, Queries: queries, Type: c.Type, + Axes: axes, } } templates := make([]*Template, len(d.Templates)) @@ -251,6 +265,13 @@ func UnmarshalDashboard(data []byte, d *chronograf.Dashboard) error { } } + axes := make(map[string]chronograf.DashboardRange, len(c.Axes)) + for a, r := range c.Axes { + axis := chronograf.DashboardRange{} + copy(axis.Bounds[:], r.Bounds[:2]) + axes[a] = axis + } + cells[i] = chronograf.DashboardCell{ ID: c.ID, X: c.X, @@ -260,6 +281,7 @@ func UnmarshalDashboard(data []byte, d *chronograf.Dashboard) error { Name: c.Name, Queries: queries, Type: c.Type, + Axes: axes, } } diff --git a/bolt/internal/internal.pb.go b/bolt/internal/internal.pb.go index ac2b29b1a4..64e5c37093 100644 --- a/bolt/internal/internal.pb.go +++ b/bolt/internal/internal.pb.go @@ -12,6 +12,7 @@ It has these top-level messages: Source Dashboard DashboardCell + DashboardRange Template TemplateValue TemplateQuery @@ -85,14 +86,15 @@ func (m *Dashboard) GetTemplates() []*Template { } type DashboardCell struct { - X int32 `protobuf:"varint,1,opt,name=x,proto3" json:"x,omitempty"` - Y int32 `protobuf:"varint,2,opt,name=y,proto3" json:"y,omitempty"` - W int32 `protobuf:"varint,3,opt,name=w,proto3" json:"w,omitempty"` - H int32 `protobuf:"varint,4,opt,name=h,proto3" json:"h,omitempty"` - Queries []*Query `protobuf:"bytes,5,rep,name=queries" json:"queries,omitempty"` - Name string `protobuf:"bytes,6,opt,name=name,proto3" json:"name,omitempty"` - Type string `protobuf:"bytes,7,opt,name=type,proto3" json:"type,omitempty"` - ID string `protobuf:"bytes,8,opt,name=ID,proto3" json:"ID,omitempty"` + X int32 `protobuf:"varint,1,opt,name=x,proto3" json:"x,omitempty"` + Y int32 `protobuf:"varint,2,opt,name=y,proto3" json:"y,omitempty"` + W int32 `protobuf:"varint,3,opt,name=w,proto3" json:"w,omitempty"` + H int32 `protobuf:"varint,4,opt,name=h,proto3" json:"h,omitempty"` + Queries []*Query `protobuf:"bytes,5,rep,name=queries" json:"queries,omitempty"` + Name string `protobuf:"bytes,6,opt,name=name,proto3" json:"name,omitempty"` + Type string `protobuf:"bytes,7,opt,name=type,proto3" json:"type,omitempty"` + ID string `protobuf:"bytes,8,opt,name=ID,proto3" json:"ID,omitempty"` + Axes map[string]*DashboardRange `protobuf:"bytes,9,rep,name=axes" json:"axes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value"` } func (m *DashboardCell) Reset() { *m = DashboardCell{} } @@ -107,6 +109,22 @@ func (m *DashboardCell) GetQueries() []*Query { return nil } +func (m *DashboardCell) GetAxes() map[string]*DashboardRange { + if m != nil { + return m.Axes + } + return nil +} + +type DashboardRange struct { + Bounds []int32 `protobuf:"varint,1,rep,name=bounds" json:"bounds,omitempty"` +} + +func (m *DashboardRange) Reset() { *m = DashboardRange{} } +func (m *DashboardRange) String() string { return proto.CompactTextString(m) } +func (*DashboardRange) ProtoMessage() {} +func (*DashboardRange) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{3} } + type Template struct { ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` TempVar string `protobuf:"bytes,2,opt,name=temp_var,json=tempVar,proto3" json:"temp_var,omitempty"` @@ -119,7 +137,7 @@ type Template struct { func (m *Template) Reset() { *m = Template{} } func (m *Template) String() string { return proto.CompactTextString(m) } func (*Template) ProtoMessage() {} -func (*Template) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{3} } +func (*Template) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{4} } func (m *Template) GetValues() []*TemplateValue { if m != nil { @@ -144,7 +162,7 @@ type TemplateValue struct { func (m *TemplateValue) Reset() { *m = TemplateValue{} } func (m *TemplateValue) String() string { return proto.CompactTextString(m) } func (*TemplateValue) ProtoMessage() {} -func (*TemplateValue) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{4} } +func (*TemplateValue) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{5} } type TemplateQuery struct { Command string `protobuf:"bytes,1,opt,name=command,proto3" json:"command,omitempty"` @@ -158,7 +176,7 @@ type TemplateQuery struct { func (m *TemplateQuery) Reset() { *m = TemplateQuery{} } func (m *TemplateQuery) String() string { return proto.CompactTextString(m) } func (*TemplateQuery) ProtoMessage() {} -func (*TemplateQuery) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{5} } +func (*TemplateQuery) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{6} } type Server struct { ID int64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"` @@ -173,7 +191,7 @@ type Server struct { func (m *Server) Reset() { *m = Server{} } func (m *Server) String() string { return proto.CompactTextString(m) } func (*Server) ProtoMessage() {} -func (*Server) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{6} } +func (*Server) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{7} } type Layout struct { ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` @@ -186,7 +204,7 @@ type Layout struct { func (m *Layout) Reset() { *m = Layout{} } func (m *Layout) String() string { return proto.CompactTextString(m) } func (*Layout) ProtoMessage() {} -func (*Layout) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{7} } +func (*Layout) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{8} } func (m *Layout) GetCells() []*Cell { if m != nil { @@ -211,7 +229,7 @@ type Cell struct { func (m *Cell) Reset() { *m = Cell{} } func (m *Cell) String() string { return proto.CompactTextString(m) } func (*Cell) ProtoMessage() {} -func (*Cell) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{8} } +func (*Cell) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{9} } func (m *Cell) GetQueries() []*Query { if m != nil { @@ -233,7 +251,7 @@ type Query struct { func (m *Query) Reset() { *m = Query{} } func (m *Query) String() string { return proto.CompactTextString(m) } func (*Query) ProtoMessage() {} -func (*Query) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{9} } +func (*Query) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{10} } func (m *Query) GetRange() *Range { if m != nil { @@ -250,7 +268,7 @@ type Range struct { func (m *Range) Reset() { *m = Range{} } func (m *Range) String() string { return proto.CompactTextString(m) } func (*Range) ProtoMessage() {} -func (*Range) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{10} } +func (*Range) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{11} } type AlertRule struct { ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` @@ -262,7 +280,7 @@ type AlertRule struct { func (m *AlertRule) Reset() { *m = AlertRule{} } func (m *AlertRule) String() string { return proto.CompactTextString(m) } func (*AlertRule) ProtoMessage() {} -func (*AlertRule) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{11} } +func (*AlertRule) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{12} } type User struct { ID uint64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"` @@ -272,12 +290,13 @@ type User struct { func (m *User) Reset() { *m = User{} } func (m *User) String() string { return proto.CompactTextString(m) } func (*User) ProtoMessage() {} -func (*User) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{12} } +func (*User) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{13} } func init() { proto.RegisterType((*Source)(nil), "internal.Source") proto.RegisterType((*Dashboard)(nil), "internal.Dashboard") proto.RegisterType((*DashboardCell)(nil), "internal.DashboardCell") + proto.RegisterType((*DashboardRange)(nil), "internal.DashboardRange") proto.RegisterType((*Template)(nil), "internal.Template") proto.RegisterType((*TemplateValue)(nil), "internal.TemplateValue") proto.RegisterType((*TemplateQuery)(nil), "internal.TemplateQuery") @@ -293,59 +312,64 @@ func init() { func init() { proto.RegisterFile("internal.proto", fileDescriptorInternal) } var fileDescriptorInternal = []byte{ - // 858 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x55, 0xdd, 0x6e, 0xe3, 0x44, - 0x14, 0xd6, 0xc4, 0x76, 0x62, 0x9f, 0xee, 0x16, 0x34, 0x5a, 0xb1, 0x06, 0x6e, 0x22, 0x0b, 0xa4, - 0x82, 0x44, 0x41, 0xec, 0x13, 0xb4, 0xb5, 0x84, 0x42, 0xbb, 0x4b, 0x99, 0xb4, 0xe5, 0x0a, 0xad, - 0x26, 0xc9, 0x49, 0x6b, 0xed, 0x24, 0x36, 0x63, 0xbb, 0x59, 0xbf, 0x02, 0x57, 0x3c, 0x01, 0x12, - 0x12, 0x57, 0x5c, 0xf2, 0x02, 0x3c, 0x04, 0x2f, 0x84, 0xce, 0xcc, 0xf8, 0x27, 0x6c, 0x41, 0x7b, - 0xb5, 0x77, 0xf3, 0x9d, 0x33, 0xf9, 0xe6, 0xfc, 0x7c, 0x9f, 0x03, 0x87, 0xd9, 0xb6, 0x42, 0xbd, - 0x95, 0xea, 0xb8, 0xd0, 0x79, 0x95, 0xf3, 0xb0, 0xc5, 0xc9, 0xcf, 0x23, 0x18, 0xcf, 0xf3, 0x5a, - 0x2f, 0x91, 0x1f, 0xc2, 0x68, 0x96, 0xc6, 0x6c, 0xca, 0x8e, 0x3c, 0x31, 0x9a, 0xa5, 0x9c, 0x83, - 0xff, 0x42, 0x6e, 0x30, 0x1e, 0x4d, 0xd9, 0x51, 0x24, 0xcc, 0x99, 0x62, 0x57, 0x4d, 0x81, 0xb1, - 0x67, 0x63, 0x74, 0xe6, 0x1f, 0x41, 0x78, 0x5d, 0x12, 0xdb, 0x06, 0x63, 0xdf, 0xc4, 0x3b, 0x4c, - 0xb9, 0x4b, 0x59, 0x96, 0xbb, 0x5c, 0xaf, 0xe2, 0xc0, 0xe6, 0x5a, 0xcc, 0xdf, 0x07, 0xef, 0x5a, - 0x5c, 0xc4, 0x63, 0x13, 0xa6, 0x23, 0x8f, 0x61, 0x92, 0xe2, 0x5a, 0xd6, 0xaa, 0x8a, 0x27, 0x53, - 0x76, 0x14, 0x8a, 0x16, 0x12, 0xcf, 0x15, 0x2a, 0xbc, 0xd5, 0x72, 0x1d, 0x87, 0x96, 0xa7, 0xc5, - 0xfc, 0x18, 0xf8, 0x6c, 0x5b, 0xe2, 0xb2, 0xd6, 0x38, 0x7f, 0x95, 0x15, 0x37, 0xa8, 0xb3, 0x75, - 0x13, 0x47, 0x86, 0xe0, 0x81, 0x0c, 0xbd, 0xf2, 0x1c, 0x2b, 0x49, 0x6f, 0x83, 0xa1, 0x6a, 0x61, - 0xf2, 0x0b, 0x83, 0x28, 0x95, 0xe5, 0xdd, 0x22, 0x97, 0x7a, 0xf5, 0x56, 0xf3, 0xf8, 0x02, 0x82, - 0x25, 0x2a, 0x55, 0xc6, 0xde, 0xd4, 0x3b, 0x3a, 0xf8, 0xfa, 0xe9, 0x71, 0x37, 0xe8, 0x8e, 0xe7, - 0x0c, 0x95, 0x12, 0xf6, 0x16, 0xff, 0x0a, 0xa2, 0x0a, 0x37, 0x85, 0x92, 0x15, 0x96, 0xb1, 0x6f, - 0x7e, 0xc2, 0xfb, 0x9f, 0x5c, 0xb9, 0x94, 0xe8, 0x2f, 0x25, 0x7f, 0x30, 0x78, 0xbc, 0x47, 0xc5, - 0x1f, 0x01, 0x7b, 0x6d, 0xaa, 0x0a, 0x04, 0x7b, 0x4d, 0xa8, 0x31, 0x15, 0x05, 0x82, 0x35, 0x84, - 0x76, 0x66, 0x37, 0x81, 0x60, 0x3b, 0x42, 0x77, 0x66, 0x23, 0x81, 0x60, 0x77, 0xfc, 0x33, 0x98, - 0xfc, 0x54, 0xa3, 0xce, 0xb0, 0x8c, 0x03, 0xf3, 0xf2, 0x7b, 0xfd, 0xcb, 0xdf, 0xd7, 0xa8, 0x1b, - 0xd1, 0xe6, 0xa9, 0x53, 0xb3, 0x4d, 0xbb, 0x1a, 0x73, 0xa6, 0x58, 0x45, 0x9b, 0x9f, 0xd8, 0x18, - 0x9d, 0xdd, 0x84, 0xec, 0x3e, 0x46, 0xb3, 0x34, 0xf9, 0x8b, 0xd1, 0x9a, 0x6c, 0xe9, 0x83, 0xf1, - 0x99, 0x24, 0xff, 0x10, 0x42, 0x6a, 0xeb, 0xe5, 0xbd, 0xd4, 0x6e, 0x84, 0x13, 0xc2, 0x37, 0x52, - 0xf3, 0x2f, 0x61, 0x7c, 0x2f, 0x55, 0x8d, 0x0f, 0x8c, 0xb1, 0xa5, 0xbb, 0xa1, 0xbc, 0x70, 0xd7, - 0xba, 0x62, 0xfc, 0x41, 0x31, 0x4f, 0x20, 0x50, 0x72, 0x81, 0xca, 0xe9, 0xcc, 0x02, 0x5a, 0x10, - 0x75, 0xd5, 0x98, 0x5e, 0x1e, 0x64, 0xb6, 0xbd, 0xdb, 0x5b, 0xc9, 0x35, 0x3c, 0xde, 0x7b, 0xb1, - 0x7b, 0x89, 0xed, 0xbf, 0x64, 0xea, 0x70, 0x6d, 0x58, 0x40, 0x12, 0x2d, 0x51, 0xe1, 0xb2, 0xc2, - 0x95, 0x59, 0x41, 0x28, 0x3a, 0x9c, 0xfc, 0xc6, 0x7a, 0x5e, 0xf3, 0x1e, 0x89, 0x70, 0x99, 0x6f, - 0x36, 0x72, 0xbb, 0x72, 0xd4, 0x2d, 0xa4, 0xb9, 0xad, 0x16, 0x8e, 0x7a, 0xb4, 0x5a, 0x10, 0xd6, - 0x85, 0x33, 0xdc, 0x48, 0x17, 0x7c, 0x0a, 0x07, 0x1b, 0x94, 0x65, 0xad, 0x71, 0x83, 0xdb, 0xca, - 0x8d, 0x60, 0x18, 0xe2, 0x4f, 0x61, 0x52, 0xc9, 0xdb, 0x97, 0xaf, 0xb0, 0x71, 0xb3, 0x18, 0x57, - 0xf2, 0xf6, 0x1c, 0x1b, 0xfe, 0x31, 0x44, 0xeb, 0x0c, 0xd5, 0xca, 0xa4, 0xec, 0x72, 0x43, 0x13, - 0x38, 0xc7, 0x26, 0xf9, 0x9d, 0xc1, 0x78, 0x8e, 0xfa, 0x1e, 0xf5, 0x5b, 0x29, 0x7f, 0xe8, 0x7a, - 0xef, 0x7f, 0x5c, 0xef, 0x3f, 0xec, 0xfa, 0xa0, 0x77, 0xfd, 0x13, 0x08, 0xe6, 0x7a, 0x39, 0x4b, - 0x4d, 0x45, 0x9e, 0xb0, 0x80, 0x7f, 0x00, 0xe3, 0x93, 0x65, 0x95, 0xdd, 0xa3, 0xfb, 0x14, 0x38, - 0x94, 0xfc, 0xca, 0x60, 0x7c, 0x21, 0x9b, 0xbc, 0xae, 0xde, 0x50, 0xd8, 0x14, 0x0e, 0x4e, 0x8a, - 0x42, 0x65, 0x4b, 0x59, 0x65, 0xf9, 0xd6, 0x55, 0x3b, 0x0c, 0xd1, 0x8d, 0xe7, 0x83, 0xd9, 0xd9, - 0xba, 0x87, 0x21, 0xfe, 0x09, 0x04, 0x67, 0xc6, 0xd0, 0xd6, 0x9d, 0x87, 0xbd, 0x5e, 0xac, 0x8f, - 0x4d, 0x92, 0x1a, 0x3c, 0xa9, 0xab, 0x7c, 0xad, 0xf2, 0x9d, 0xe9, 0x24, 0x14, 0x1d, 0x4e, 0xfe, - 0x66, 0xe0, 0xbf, 0x2b, 0xa3, 0x3e, 0x02, 0x96, 0xb9, 0x45, 0xb2, 0xac, 0xb3, 0xed, 0x64, 0x60, - 0xdb, 0x18, 0x26, 0x8d, 0x96, 0xdb, 0x5b, 0x2c, 0xe3, 0x70, 0xea, 0x1d, 0x79, 0xa2, 0x85, 0x26, - 0x63, 0x3c, 0x52, 0xc6, 0xd1, 0xd4, 0x23, 0x05, 0x3a, 0xd8, 0x69, 0x1e, 0x7a, 0xcd, 0x27, 0x7f, - 0x32, 0x08, 0x3a, 0xe5, 0x9e, 0xed, 0x2b, 0xf7, 0xac, 0x57, 0x6e, 0x7a, 0xda, 0x2a, 0x37, 0x3d, - 0x25, 0x2c, 0x2e, 0x5b, 0xe5, 0x8a, 0x4b, 0x9a, 0xda, 0x37, 0x3a, 0xaf, 0x8b, 0xd3, 0xc6, 0x8e, - 0x37, 0x12, 0x1d, 0xa6, 0x75, 0xff, 0x70, 0x87, 0xda, 0xf5, 0x1c, 0x09, 0x87, 0x48, 0x1c, 0x17, - 0xc6, 0xd5, 0xb6, 0x4b, 0x0b, 0xf8, 0xa7, 0x10, 0x08, 0xea, 0xc2, 0xb4, 0xba, 0x37, 0x20, 0x13, - 0x16, 0x36, 0x9b, 0x3c, 0x73, 0xd7, 0x88, 0xe5, 0xba, 0x28, 0x50, 0x3b, 0x4d, 0x5b, 0x60, 0xb8, - 0xf3, 0x1d, 0xda, 0xcf, 0x91, 0x27, 0x2c, 0x48, 0x7e, 0x84, 0xe8, 0x44, 0xa1, 0xae, 0x44, 0xad, - 0xde, 0xfc, 0x88, 0x71, 0xf0, 0xbf, 0x9d, 0x7f, 0xf7, 0xa2, 0x75, 0x02, 0x9d, 0x7b, 0xfd, 0x7a, - 0xff, 0xd2, 0xef, 0xb9, 0x2c, 0xe4, 0x2c, 0x35, 0x8b, 0xf5, 0x84, 0x43, 0xc9, 0xe7, 0xe0, 0x93, - 0x4f, 0x06, 0xcc, 0xfe, 0x7f, 0x79, 0x6c, 0x31, 0x36, 0xff, 0xd6, 0xcf, 0xfe, 0x09, 0x00, 0x00, - 0xff, 0xff, 0xa7, 0xc6, 0x53, 0x22, 0xbf, 0x07, 0x00, 0x00, + // 937 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xbc, 0x56, 0x51, 0x8f, 0xdb, 0x44, + 0x10, 0xd6, 0xc6, 0x76, 0x12, 0xcf, 0xb5, 0x07, 0x5a, 0x55, 0x74, 0x29, 0x2f, 0xc1, 0x02, 0x29, + 0x20, 0x11, 0x50, 0x2b, 0x24, 0xc4, 0x5b, 0x7a, 0x41, 0x28, 0xdc, 0xb5, 0x5c, 0x37, 0x77, 0xc7, + 0x13, 0xaa, 0x36, 0xc9, 0xe4, 0xce, 0xaa, 0x13, 0x9b, 0xb5, 0x7d, 0x39, 0xff, 0x05, 0x9e, 0xf8, + 0x05, 0x48, 0x48, 0xfc, 0x02, 0xc4, 0x3b, 0x3f, 0x82, 0x3f, 0x84, 0x66, 0x77, 0x6d, 0x27, 0x34, + 0x45, 0x7d, 0xe2, 0xe9, 0xf6, 0x9b, 0xd9, 0xcc, 0x78, 0xbf, 0xf9, 0xe6, 0xd3, 0xc1, 0x71, 0xbc, + 0x29, 0x50, 0x6f, 0x54, 0x32, 0xca, 0x74, 0x5a, 0xa4, 0xbc, 0x5f, 0xe3, 0xe8, 0xe7, 0x0e, 0x74, + 0x67, 0x69, 0xa9, 0x17, 0xc8, 0x8f, 0xa1, 0x33, 0x9d, 0x08, 0x36, 0x60, 0x43, 0x4f, 0x76, 0xa6, + 0x13, 0xce, 0xc1, 0x7f, 0xae, 0xd6, 0x28, 0x3a, 0x03, 0x36, 0x0c, 0xa5, 0x39, 0x53, 0xec, 0xa2, + 0xca, 0x50, 0x78, 0x36, 0x46, 0x67, 0xfe, 0x08, 0xfa, 0x97, 0x39, 0x55, 0x5b, 0xa3, 0xf0, 0x4d, + 0xbc, 0xc1, 0x94, 0x3b, 0x57, 0x79, 0xbe, 0x4d, 0xf5, 0x52, 0x04, 0x36, 0x57, 0x63, 0xfe, 0x2e, + 0x78, 0x97, 0xf2, 0x4c, 0x74, 0x4d, 0x98, 0x8e, 0x5c, 0x40, 0x6f, 0x82, 0x2b, 0x55, 0x26, 0x85, + 0xe8, 0x0d, 0xd8, 0xb0, 0x2f, 0x6b, 0x48, 0x75, 0x2e, 0x30, 0xc1, 0x6b, 0xad, 0x56, 0xa2, 0x6f, + 0xeb, 0xd4, 0x98, 0x8f, 0x80, 0x4f, 0x37, 0x39, 0x2e, 0x4a, 0x8d, 0xb3, 0x57, 0x71, 0x76, 0x85, + 0x3a, 0x5e, 0x55, 0x22, 0x34, 0x05, 0x0e, 0x64, 0xa8, 0xcb, 0x33, 0x2c, 0x14, 0xf5, 0x06, 0x53, + 0xaa, 0x86, 0xd1, 0x2f, 0x0c, 0xc2, 0x89, 0xca, 0x6f, 0xe6, 0xa9, 0xd2, 0xcb, 0xb7, 0xe2, 0xe3, + 0x33, 0x08, 0x16, 0x98, 0x24, 0xb9, 0xf0, 0x06, 0xde, 0xf0, 0xe8, 0xf1, 0xc3, 0x51, 0x43, 0x74, + 0x53, 0xe7, 0x04, 0x93, 0x44, 0xda, 0x5b, 0xfc, 0x0b, 0x08, 0x0b, 0x5c, 0x67, 0x89, 0x2a, 0x30, + 0x17, 0xbe, 0xf9, 0x09, 0x6f, 0x7f, 0x72, 0xe1, 0x52, 0xb2, 0xbd, 0x14, 0xfd, 0xd9, 0x81, 0xfb, + 0x7b, 0xa5, 0xf8, 0x3d, 0x60, 0x77, 0xe6, 0xab, 0x02, 0xc9, 0xee, 0x08, 0x55, 0xe6, 0x8b, 0x02, + 0xc9, 0x2a, 0x42, 0x5b, 0x33, 0x9b, 0x40, 0xb2, 0x2d, 0xa1, 0x1b, 0x33, 0x91, 0x40, 0xb2, 0x1b, + 0xfe, 0x09, 0xf4, 0x7e, 0x2a, 0x51, 0xc7, 0x98, 0x8b, 0xc0, 0x74, 0x7e, 0xa7, 0xed, 0xfc, 0xa2, + 0x44, 0x5d, 0xc9, 0x3a, 0x4f, 0x2f, 0x35, 0xd3, 0xb4, 0xa3, 0x31, 0x67, 0x8a, 0x15, 0x34, 0xf9, + 0x9e, 0x8d, 0xd1, 0xd9, 0x31, 0x64, 0xe7, 0x41, 0x0c, 0x7d, 0x09, 0xbe, 0xba, 0xc3, 0x5c, 0x84, + 0xa6, 0xfe, 0x87, 0x6f, 0x20, 0x63, 0x34, 0xbe, 0xc3, 0xfc, 0x9b, 0x4d, 0xa1, 0x2b, 0x69, 0xae, + 0x3f, 0x7a, 0x01, 0x61, 0x13, 0x22, 0x55, 0xbc, 0xc2, 0xca, 0x3c, 0x30, 0x94, 0x74, 0xe4, 0x23, + 0x08, 0x6e, 0x55, 0x52, 0x5a, 0xe2, 0x8f, 0x1e, 0x8b, 0x03, 0x65, 0xa5, 0xda, 0x5c, 0xa3, 0xb4, + 0xd7, 0xbe, 0xee, 0x7c, 0xc5, 0xa2, 0x21, 0x1c, 0xef, 0x27, 0xf9, 0x7b, 0xd0, 0x9d, 0xa7, 0xe5, + 0x66, 0x99, 0x0b, 0x36, 0xf0, 0x86, 0x81, 0x74, 0x28, 0xfa, 0x8b, 0x91, 0xb4, 0x2c, 0xdd, 0x3b, + 0x23, 0xb7, 0x0f, 0x7a, 0x1f, 0xfa, 0x34, 0x8a, 0x97, 0xb7, 0x4a, 0xbb, 0xb1, 0xf7, 0x08, 0x5f, + 0x29, 0xcd, 0x3f, 0x87, 0xae, 0x69, 0x77, 0x60, 0xf4, 0x75, 0xb9, 0x2b, 0xca, 0x4b, 0x77, 0xad, + 0x21, 0xd0, 0xdf, 0x21, 0xf0, 0x01, 0x04, 0x89, 0x9a, 0x63, 0xe2, 0x76, 0xc3, 0x02, 0x12, 0x15, + 0x4d, 0xa2, 0x32, 0xfc, 0x1f, 0xac, 0x6c, 0xe7, 0x65, 0x6f, 0x45, 0x97, 0x70, 0x7f, 0xaf, 0x63, + 0xd3, 0x89, 0xed, 0x77, 0x6a, 0x49, 0x0c, 0x1d, 0x55, 0xb4, 0x56, 0x39, 0x26, 0xb8, 0x28, 0x70, + 0x69, 0x64, 0xd3, 0x97, 0x0d, 0x8e, 0x7e, 0x63, 0x6d, 0x5d, 0xd3, 0x8f, 0x16, 0x67, 0x91, 0xae, + 0xd7, 0x6a, 0xb3, 0x74, 0xa5, 0x6b, 0x48, 0xbc, 0x2d, 0xe7, 0xae, 0x74, 0x67, 0x39, 0x27, 0xac, + 0x33, 0x67, 0x12, 0x1d, 0x9d, 0xf1, 0x01, 0x1c, 0xad, 0x51, 0xe5, 0xa5, 0xc6, 0x35, 0x6e, 0x0a, + 0x47, 0xc1, 0x6e, 0x88, 0x3f, 0x84, 0x5e, 0xa1, 0xae, 0x5f, 0xd2, 0xe8, 0x2d, 0x17, 0xdd, 0x42, + 0x5d, 0x9f, 0x62, 0xc5, 0x3f, 0x80, 0x70, 0x15, 0x63, 0xb2, 0x34, 0x29, 0x2b, 0xc8, 0xbe, 0x09, + 0x9c, 0x62, 0x15, 0xfd, 0xce, 0xa0, 0x3b, 0x43, 0x7d, 0x8b, 0xfa, 0xad, 0xb6, 0x75, 0xd7, 0xa9, + 0xbc, 0xff, 0x70, 0x2a, 0xff, 0xb0, 0x53, 0x05, 0xad, 0x53, 0x3d, 0x80, 0x60, 0xa6, 0x17, 0xd3, + 0x89, 0xf9, 0x22, 0x4f, 0x5a, 0x40, 0x1a, 0x1b, 0x2f, 0x8a, 0xf8, 0x16, 0x9d, 0x7d, 0x39, 0x14, + 0xfd, 0xca, 0xa0, 0x7b, 0xa6, 0xaa, 0xb4, 0x2c, 0x5e, 0x53, 0xd8, 0x00, 0x8e, 0xc6, 0x59, 0x96, + 0xc4, 0x0b, 0x55, 0xc4, 0xe9, 0xc6, 0x7d, 0xed, 0x6e, 0x88, 0x6e, 0x3c, 0xdb, 0xe1, 0xce, 0x7e, + 0xf7, 0x6e, 0x88, 0x7f, 0x04, 0xc1, 0x89, 0x31, 0x21, 0xeb, 0x28, 0xc7, 0xad, 0x5e, 0xac, 0xf7, + 0x98, 0x24, 0x3d, 0x70, 0x5c, 0x16, 0xe9, 0x2a, 0x49, 0xb7, 0xe6, 0x25, 0x7d, 0xd9, 0xe0, 0xe8, + 0x6f, 0x06, 0xfe, 0xff, 0x65, 0x2e, 0xf7, 0x80, 0xc5, 0x6e, 0x90, 0x2c, 0x6e, 0xac, 0xa6, 0xb7, + 0x63, 0x35, 0x02, 0x7a, 0x95, 0xa6, 0xa5, 0xcd, 0x45, 0x7f, 0xe0, 0x0d, 0x3d, 0x59, 0x43, 0x93, + 0x31, 0x3b, 0x62, 0x3d, 0x26, 0x94, 0x35, 0x6c, 0x34, 0x0f, 0xad, 0xe6, 0xa3, 0x3f, 0x18, 0x04, + 0x8d, 0x72, 0x4f, 0xf6, 0x95, 0x7b, 0xd2, 0x2a, 0x77, 0xf2, 0xb4, 0x56, 0xee, 0xe4, 0x29, 0x61, + 0x79, 0x5e, 0x2b, 0x57, 0x9e, 0x13, 0x6b, 0xdf, 0xea, 0xb4, 0xcc, 0x9e, 0x56, 0x96, 0xde, 0x50, + 0x36, 0x98, 0xc6, 0xfd, 0xc3, 0x0d, 0x6a, 0xf7, 0xe6, 0x50, 0x3a, 0x44, 0xe2, 0x38, 0x33, 0x5b, + 0x6d, 0x5f, 0x69, 0x01, 0xff, 0x18, 0x02, 0xe3, 0x44, 0xe6, 0xa9, 0x7b, 0x04, 0x39, 0xf7, 0x32, + 0x7f, 0xa2, 0x27, 0xee, 0x1a, 0x55, 0xb9, 0xcc, 0x32, 0xd4, 0x4e, 0xd3, 0x16, 0x98, 0xda, 0xe9, + 0x16, 0xad, 0x1d, 0x79, 0xd2, 0x82, 0xe8, 0x47, 0x08, 0xc7, 0x09, 0xea, 0x42, 0x96, 0xc9, 0xeb, + 0x26, 0xc6, 0xc1, 0xff, 0x6e, 0xf6, 0xfd, 0xf3, 0x7a, 0x13, 0xe8, 0xdc, 0xea, 0xd7, 0xfb, 0x97, + 0x7e, 0x4f, 0x55, 0xa6, 0xa6, 0x13, 0x33, 0x58, 0x4f, 0x3a, 0x14, 0x7d, 0x0a, 0x3e, 0xed, 0xc9, + 0x4e, 0x65, 0xff, 0x4d, 0x3b, 0x36, 0xef, 0x9a, 0xff, 0x30, 0x9e, 0xfc, 0x13, 0x00, 0x00, 0xff, + 0xff, 0xa5, 0xc4, 0x18, 0xb5, 0x73, 0x08, 0x00, 0x00, } diff --git a/bolt/internal/internal.proto b/bolt/internal/internal.proto index 40a305718e..95294dc4a3 100644 --- a/bolt/internal/internal.proto +++ b/bolt/internal/internal.proto @@ -22,14 +22,19 @@ message Dashboard { } message DashboardCell { - int32 x = 1; // X-coordinate of Cell in the Dashboard - int32 y = 2; // Y-coordinate of Cell in the Dashboard - int32 w = 3; // Width of Cell in the Dashboard - int32 h = 4; // Height of Cell in the Dashboard - repeated Query queries = 5; // Time-series data queries for Dashboard - string name = 6; // User-facing name for this Dashboard - string type = 7; // Dashboard visualization type - string ID = 8; // id is the unique id of the dashboard. MIGRATED FIELD added in 1.2.0-beta6 + int32 x = 1; // X-coordinate of Cell in the Dashboard + int32 y = 2; // Y-coordinate of Cell in the Dashboard + int32 w = 3; // Width of Cell in the Dashboard + int32 h = 4; // Height of Cell in the Dashboard + repeated Query queries = 5; // Time-series data queries for Dashboard + string name = 6; // User-facing name for this Dashboard + string type = 7; // Dashboard visualization type + string ID = 8; // id is the unique id of the dashboard. MIGRATED FIELD added in 1.2.0-beta6 + map axes = 9; // Axes represent the graphical viewport for a cell's visualizations +} + +message DashboardRange { + repeated int32 bounds = 1; // bounds are an ordered 2-tuple consisting of lower and upper axis extents, respectively } message Template { diff --git a/chronograf.go b/chronograf.go index 233351684c..e3993c58ea 100644 --- a/chronograf.go +++ b/chronograf.go @@ -565,16 +565,22 @@ type Dashboard struct { Name string `json:"name"` } +// DashboardRange represents the visible extents of a visualization +type DashboardRange struct { + Bounds [2]int32 `json:"bounds"` // bounds are an ordered 2-tuple consisting of lower and upper axis extents, respectively +} + // DashboardCell holds visual and query information for a cell type DashboardCell struct { - ID string `json:"i"` - X int32 `json:"x"` - Y int32 `json:"y"` - W int32 `json:"w"` - H int32 `json:"h"` - Name string `json:"name"` - Queries []DashboardQuery `json:"queries"` - Type string `json:"type"` + ID string `json:"i"` + X int32 `json:"x"` + Y int32 `json:"y"` + W int32 `json:"w"` + H int32 `json:"h"` + Name string `json:"name"` + Queries []DashboardQuery `json:"queries"` + Axes map[string]DashboardRange `json:"axes"` + Type string `json:"type"` } // DashboardsStore is the storage and retrieval of dashboards From a423a98d3623e8bdff60d3f2f19b1711aea26e67 Mon Sep 17 00:00:00 2001 From: Tim Raymond Date: Wed, 19 Jul 2017 11:18:01 -0400 Subject: [PATCH 02/93] Update Swagger with Cell Axes Cells now have axes which represent their visualization's viewport. This updates the Swagger documentation to reflect this. Things to be aware of ===================== The form of "axes" is that of a map, which is represented in Swagger by an "additionalProperties" key (search for "string to model mapping" here: https://swagger.io/specification/). --- server/swagger.json | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/server/swagger.json b/server/swagger.json index 769a6be2c9..524e5c8192 100644 --- a/server/swagger.json +++ b/server/swagger.json @@ -3999,6 +3999,13 @@ "$ref": "#/definitions/DashboardQuery" } }, + "axes": { + "description": "The viewport for a cell's visualizations", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/DashboardRange" + } + }, "type": { "description": "Cell visualization type", "type": "string", @@ -4080,6 +4087,22 @@ } } }, + "DashboardRange": { + "type": "object", + "description": "A description of a particular axis for a visualization", + "properties": { + "bounds": { + "type": "array", + "minItems": 0, + "maxItems": 2, + "description": "The extents of an axis in the form [lower, upper]. Clients determine whether bounds are to be inclusive or exclusive of their limits", + "items": { + "type": "integer", + "format": "int32" + } + } + } + }, "Routes": { "type": "object", "properties": { From 617fc38b14259f57fca755489238e58d565416af Mon Sep 17 00:00:00 2001 From: Tim Raymond Date: Thu, 20 Jul 2017 14:39:19 -0400 Subject: [PATCH 03/93] s/DashboardRange/Axis, format protobufs, 64-bit "Axis" is a more consistent and appropriate name. Also, the formatting the protobufs was all over the place, so this has been made consistent using a first-column \t. Furthermore, a vim modeline was added to the bottom to make it easier for editors to autoconfigure themselves to the right format, since protobufs are not something that we edit everyday. Also, 32-bit values have been substituted for 64-bit values in Protobuf definitions. --- bolt/internal/internal.go | 4 +- bolt/internal/internal.proto | 121 ++++++++++++++++++----------------- chronograf.go | 24 +++---- 3 files changed, 77 insertions(+), 72 deletions(-) diff --git a/bolt/internal/internal.go b/bolt/internal/internal.go index 4b1f7b5e75..a9ef81cd57 100644 --- a/bolt/internal/internal.go +++ b/bolt/internal/internal.go @@ -265,9 +265,9 @@ func UnmarshalDashboard(data []byte, d *chronograf.Dashboard) error { } } - axes := make(map[string]chronograf.DashboardRange, len(c.Axes)) + axes := make(map[string]chronograf.Axis, len(c.Axes)) for a, r := range c.Axes { - axis := chronograf.DashboardRange{} + axis := chronograf.Axis{} copy(axis.Bounds[:], r.Bounds[:2]) axes[a] = axis } diff --git a/bolt/internal/internal.proto b/bolt/internal/internal.proto index 95294dc4a3..2b5981363a 100644 --- a/bolt/internal/internal.proto +++ b/bolt/internal/internal.proto @@ -18,47 +18,47 @@ message Dashboard { int64 ID = 1; // ID is the unique ID of the dashboard string Name = 2; // Name is the user-defined name of the dashboard repeated DashboardCell cells = 3; // a representation of all visual data required for rendering the dashboard - repeated Template templates = 4; // Templates replace template variables within InfluxQL + repeated Template templates = 4; // Templates replace template variables within InfluxQL } message DashboardCell { - int32 x = 1; // X-coordinate of Cell in the Dashboard - int32 y = 2; // Y-coordinate of Cell in the Dashboard - int32 w = 3; // Width of Cell in the Dashboard - int32 h = 4; // Height of Cell in the Dashboard - repeated Query queries = 5; // Time-series data queries for Dashboard - string name = 6; // User-facing name for this Dashboard - string type = 7; // Dashboard visualization type - string ID = 8; // id is the unique id of the dashboard. MIGRATED FIELD added in 1.2.0-beta6 - map axes = 9; // Axes represent the graphical viewport for a cell's visualizations + int32 x = 1; // X-coordinate of Cell in the Dashboard + int32 y = 2; // Y-coordinate of Cell in the Dashboard + int32 w = 3; // Width of Cell in the Dashboard + int32 h = 4; // Height of Cell in the Dashboard + repeated Query queries = 5; // Time-series data queries for Dashboard + string name = 6; // User-facing name for this Dashboard + string type = 7; // Dashboard visualization type + string ID = 8; // id is the unique id of the dashboard. MIGRATED FIELD added in 1.2.0-beta6 + map axes = 9; // Axes represent the graphical viewport for a cell's visualizations } -message DashboardRange { - repeated int32 bounds = 1; // bounds are an ordered 2-tuple consisting of lower and upper axis extents, respectively +message Axis { + repeated int64 bounds = 1; // bounds are an ordered 2-tuple consisting of lower and upper axis extents, respectively } message Template { - string ID = 1; // ID is the unique ID associated with this template - string temp_var = 2; - repeated TemplateValue values = 3; - string type = 4; // Type can be fieldKeys, tagKeys, tagValues, CSV, constant, query, measurements, databases - string label = 5; // Label is a user-facing description of the Template - TemplateQuery query = 6; // Query is used to generate the choices for a template + string ID = 1; // ID is the unique ID associated with this template + string temp_var = 2; + repeated TemplateValue values = 3; + string type = 4; // Type can be fieldKeys, tagKeys, tagValues, CSV, constant, query, measurements, databases + string label = 5; // Label is a user-facing description of the Template + TemplateQuery query = 6; // Query is used to generate the choices for a template } message TemplateValue { - string type = 1; // Type can be tagKey, tagValue, fieldKey, csv, measurement, database, constant - string value = 2; // Value is the specific value used to replace a template in an InfluxQL query - bool selected = 3; // Selected states that this variable has been picked to use for replacement + string type = 1; // Type can be tagKey, tagValue, fieldKey, csv, measurement, database, constant + string value = 2; // Value is the specific value used to replace a template in an InfluxQL query + bool selected = 3; // Selected states that this variable has been picked to use for replacement } message TemplateQuery { - string command = 1; // Command is the query itself - string db = 2; // DB the database for the query (optional) - string rp = 3; // RP is a retention policy and optional; - string measurement = 4; // Measurement is the optinally selected measurement for the query - string tag_key = 5; // TagKey is the optionally selected tag key for the query - string field_key = 6; // FieldKey is the optionally selected field key for the query + string command = 1; // Command is the query itself + string db = 2; // DB the database for the query (optional) + string rp = 3; // RP is a retention policy and optional; + string measurement = 4; // Measurement is the optinally selected measurement for the query + string tag_key = 5; // TagKey is the optionally selected tag key for the query + string field_key = 6; // FieldKey is the optionally selected field key for the query } message Server { @@ -67,54 +67,59 @@ message Server { string Username = 3; // Username is the username to connect to the server string Password = 4; string URL = 5; // URL is the path to the server - int64 SrcID = 6; // SrcID is the ID of the data source + int64 SrcID = 6; // SrcID is the ID of the data source bool Active = 7; // is this the currently active server for the source } message Layout { - string ID = 1; // ID is the unique ID of the layout. - string Application = 2; // Application is the user facing name of this Layout. - string Measurement = 3; // Measurement is the descriptive name of the time series data. - repeated Cell Cells = 4; // Cells are the individual visualization elements. - bool Autoflow = 5; // Autoflow indicates whether the frontend should layout the cells automatically. + string ID = 1; // ID is the unique ID of the layout. + string Application = 2; // Application is the user facing name of this Layout. + string Measurement = 3; // Measurement is the descriptive name of the time series data. + repeated Cell Cells = 4; // Cells are the individual visualization elements. + bool Autoflow = 5; // Autoflow indicates whether the frontend should layout the cells automatically. } message Cell { - int32 x = 1; // X-coordinate of Cell in the Layout - int32 y = 2; // Y-coordinate of Cell in the Layout - int32 w = 3; // Width of Cell in the Layout - int32 h = 4; // Height of Cell in the Layout - repeated Query queries = 5; // Time-series data queries for Cell. - string i = 6; // Unique identifier for the cell - string name = 7; // User-facing name for this cell - repeated int64 yranges = 8; // Limits of the y-axes - repeated string ylabels = 9; // Labels of the y-axes - string type = 10; // Cell visualization type + int32 x = 1; // X-coordinate of Cell in the Layout + int32 y = 2; // Y-coordinate of Cell in the Layout + int32 w = 3; // Width of Cell in the Layout + int32 h = 4; // Height of Cell in the Layout + repeated Query queries = 5; // Time-series data queries for Cell. + string i = 6; // Unique identifier for the cell + string name = 7; // User-facing name for this cell + repeated int64 yranges = 8; // Limits of the y-axes + repeated string ylabels = 9; // Labels of the y-axes + string type = 10; // Cell visualization type } message Query { - string Command = 1; // Command is the query itself - string DB = 2; // DB the database for the query (optional) - string RP = 3; // RP is a retention policy and optional; - repeated string GroupBys= 4; // GroupBys define the groups to combine in the query - repeated string Wheres = 5; // Wheres define the restrictions on the query - string Label = 6; // Label is the name of the Y-Axis - Range Range = 7; // Range is the upper and lower bound of the Y-Axis + string Command = 1; // Command is the query itself + string DB = 2; // DB the database for the query (optional) + string RP = 3; // RP is a retention policy and optional; + repeated string GroupBys= 4; // GroupBys define the groups to combine in the query + repeated string Wheres = 5; // Wheres define the restrictions on the query + string Label = 6; // Label is the name of the Y-Axis + Range Range = 7; // Range is the upper and lower bound of the Y-Axis } message Range { - int64 Upper = 1; // Upper is the upper-bound of the range - int64 Lower = 2; // Lower is the lower-bound of the range + int64 Upper = 1; // Upper is the upper-bound of the range + int64 Lower = 2; // Lower is the lower-bound of the range } message AlertRule { - string ID = 1; // ID is the unique ID of this alert rule - string JSON = 2; // JSON byte representation of the alert - int64 SrcID = 3; // SrcID is the id of the source this alert is associated with - int64 KapaID = 4; // KapaID is the id of the kapacitor this alert is associated with + string ID = 1; // ID is the unique ID of this alert rule + string JSON = 2; // JSON byte representation of the alert + int64 SrcID = 3; // SrcID is the id of the source this alert is associated with + int64 KapaID = 4; // KapaID is the id of the kapacitor this alert is associated with } message User { - uint64 ID = 1; // ID is the unique ID of this user - string Name = 2; // Name is the user's login name + uint64 ID = 1; // ID is the unique ID of this user + string Name = 2; // Name is the user's login name } + +// The following is a vim modeline, it autoconfigures vim to have the +// appropriate tabbing and whitespace management to edit this file +// +// vim: ai:ts=4:noet:sts=4 diff --git a/chronograf.go b/chronograf.go index e3993c58ea..c1401790dc 100644 --- a/chronograf.go +++ b/chronograf.go @@ -565,22 +565,22 @@ type Dashboard struct { Name string `json:"name"` } -// DashboardRange represents the visible extents of a visualization -type DashboardRange struct { - Bounds [2]int32 `json:"bounds"` // bounds are an ordered 2-tuple consisting of lower and upper axis extents, respectively +// Axis represents the visible extents of a visualization +type Axis struct { + Bounds [2]int64 `json:"bounds"` // bounds are an ordered 2-tuple consisting of lower and upper axis extents, respectively } // DashboardCell holds visual and query information for a cell type DashboardCell struct { - ID string `json:"i"` - X int32 `json:"x"` - Y int32 `json:"y"` - W int32 `json:"w"` - H int32 `json:"h"` - Name string `json:"name"` - Queries []DashboardQuery `json:"queries"` - Axes map[string]DashboardRange `json:"axes"` - Type string `json:"type"` + ID string `json:"i"` + X int32 `json:"x"` + Y int32 `json:"y"` + W int32 `json:"w"` + H int32 `json:"h"` + Name string `json:"name"` + Queries []DashboardQuery `json:"queries"` + Axes map[string]Axis `json:"axes"` + Type string `json:"type"` } // DashboardsStore is the storage and retrieval of dashboards From d1443b1dbdbac5893f80d7c8a40fe5cfc181c58f Mon Sep 17 00:00:00 2001 From: Tim Raymond Date: Fri, 21 Jul 2017 11:08:52 -0400 Subject: [PATCH 04/93] Add tests for Dashboard protobuf & misc renaming There were previously no tests around Dashboard serialization to protobuf, so this patch adds coverage for that. Also, the `go-cmp` package has been introduced to replace our usage of `reflect.DeepEqual` going forward because it has better comparison features that make it more stable across Go versions and produces nice diffs in tests when they fail, reducing the need for debug lines and manual inspection. --- Gopkg.lock | 7 +- bolt/internal/internal.go | 6 +- bolt/internal/internal.pb.go | 156 ++++++++++++++++----------------- bolt/internal/internal_test.go | 43 +++++++++ 4 files changed, 130 insertions(+), 82 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 508227b857..d8021c0107 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -47,6 +47,11 @@ packages = ["proto"] revision = "8ee79997227bf9b34611aee7946ae64735e6fd93" +[[projects]] + name = "github.com/google/go-cmp" + packages = ["cmp"] + revision = "79b2d888f100ec053545168aa94bcfb322e8bfc8" + [[projects]] name = "github.com/google/go-github" packages = ["github"] @@ -135,6 +140,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "bac138180cd86a0ae604cd3aa7b6ba300673478c880882bd58a4bd7f8bff518d" + inputs-digest = "f34fb88755292baba8b52c14bf5b9a028daff96a763368a7cf1de90004d33695" solver-name = "gps-cdcl" solver-version = 1 diff --git a/bolt/internal/internal.go b/bolt/internal/internal.go index a9ef81cd57..b9400a4899 100644 --- a/bolt/internal/internal.go +++ b/bolt/internal/internal.go @@ -179,15 +179,15 @@ func MarshalDashboard(d chronograf.Dashboard) ([]byte, error) { } } - axes := make(map[string]*DashboardRange, len(c.Axes)) + axes := make(map[string]*Axis, len(c.Axes)) for a, r := range c.Axes { // need to explicitly allocate a new array because r.Bounds is // over-written and the resulting slices from previous iterations will // point to later iteration's data. It is _not_ enough to simply re-slice // r.Bounds - axis := [2]int32{} + axis := [2]int64{} copy(axis[:], r.Bounds[:2]) - axes[a] = &DashboardRange{ + axes[a] = &Axis{ Bounds: axis[:], } } diff --git a/bolt/internal/internal.pb.go b/bolt/internal/internal.pb.go index 64e5c37093..ccc0aedc9d 100644 --- a/bolt/internal/internal.pb.go +++ b/bolt/internal/internal.pb.go @@ -12,7 +12,7 @@ It has these top-level messages: Source Dashboard DashboardCell - DashboardRange + Axis Template TemplateValue TemplateQuery @@ -86,15 +86,15 @@ func (m *Dashboard) GetTemplates() []*Template { } type DashboardCell struct { - X int32 `protobuf:"varint,1,opt,name=x,proto3" json:"x,omitempty"` - Y int32 `protobuf:"varint,2,opt,name=y,proto3" json:"y,omitempty"` - W int32 `protobuf:"varint,3,opt,name=w,proto3" json:"w,omitempty"` - H int32 `protobuf:"varint,4,opt,name=h,proto3" json:"h,omitempty"` - Queries []*Query `protobuf:"bytes,5,rep,name=queries" json:"queries,omitempty"` - Name string `protobuf:"bytes,6,opt,name=name,proto3" json:"name,omitempty"` - Type string `protobuf:"bytes,7,opt,name=type,proto3" json:"type,omitempty"` - ID string `protobuf:"bytes,8,opt,name=ID,proto3" json:"ID,omitempty"` - Axes map[string]*DashboardRange `protobuf:"bytes,9,rep,name=axes" json:"axes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value"` + X int32 `protobuf:"varint,1,opt,name=x,proto3" json:"x,omitempty"` + Y int32 `protobuf:"varint,2,opt,name=y,proto3" json:"y,omitempty"` + W int32 `protobuf:"varint,3,opt,name=w,proto3" json:"w,omitempty"` + H int32 `protobuf:"varint,4,opt,name=h,proto3" json:"h,omitempty"` + Queries []*Query `protobuf:"bytes,5,rep,name=queries" json:"queries,omitempty"` + Name string `protobuf:"bytes,6,opt,name=name,proto3" json:"name,omitempty"` + Type string `protobuf:"bytes,7,opt,name=type,proto3" json:"type,omitempty"` + ID string `protobuf:"bytes,8,opt,name=ID,proto3" json:"ID,omitempty"` + Axes map[string]*Axis `protobuf:"bytes,9,rep,name=axes" json:"axes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value"` } func (m *DashboardCell) Reset() { *m = DashboardCell{} } @@ -109,21 +109,21 @@ func (m *DashboardCell) GetQueries() []*Query { return nil } -func (m *DashboardCell) GetAxes() map[string]*DashboardRange { +func (m *DashboardCell) GetAxes() map[string]*Axis { if m != nil { return m.Axes } return nil } -type DashboardRange struct { - Bounds []int32 `protobuf:"varint,1,rep,name=bounds" json:"bounds,omitempty"` +type Axis struct { + Bounds []int64 `protobuf:"varint,1,rep,name=bounds" json:"bounds,omitempty"` } -func (m *DashboardRange) Reset() { *m = DashboardRange{} } -func (m *DashboardRange) String() string { return proto.CompactTextString(m) } -func (*DashboardRange) ProtoMessage() {} -func (*DashboardRange) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{3} } +func (m *Axis) Reset() { *m = Axis{} } +func (m *Axis) String() string { return proto.CompactTextString(m) } +func (*Axis) ProtoMessage() {} +func (*Axis) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{3} } type Template struct { ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` @@ -296,7 +296,7 @@ func init() { proto.RegisterType((*Source)(nil), "internal.Source") proto.RegisterType((*Dashboard)(nil), "internal.Dashboard") proto.RegisterType((*DashboardCell)(nil), "internal.DashboardCell") - proto.RegisterType((*DashboardRange)(nil), "internal.DashboardRange") + proto.RegisterType((*Axis)(nil), "internal.Axis") proto.RegisterType((*Template)(nil), "internal.Template") proto.RegisterType((*TemplateValue)(nil), "internal.TemplateValue") proto.RegisterType((*TemplateQuery)(nil), "internal.TemplateQuery") @@ -312,64 +312,64 @@ func init() { func init() { proto.RegisterFile("internal.proto", fileDescriptorInternal) } var fileDescriptorInternal = []byte{ - // 937 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xbc, 0x56, 0x51, 0x8f, 0xdb, 0x44, - 0x10, 0xd6, 0xc6, 0x76, 0x12, 0xcf, 0xb5, 0x07, 0x5a, 0x55, 0x74, 0x29, 0x2f, 0xc1, 0x02, 0x29, - 0x20, 0x11, 0x50, 0x2b, 0x24, 0xc4, 0x5b, 0x7a, 0x41, 0x28, 0xdc, 0xb5, 0x5c, 0x37, 0x77, 0xc7, - 0x13, 0xaa, 0x36, 0xc9, 0xe4, 0xce, 0xaa, 0x13, 0x9b, 0xb5, 0x7d, 0x39, 0xff, 0x05, 0x9e, 0xf8, - 0x05, 0x48, 0x48, 0xfc, 0x02, 0xc4, 0x3b, 0x3f, 0x82, 0x3f, 0x84, 0x66, 0x77, 0x6d, 0x27, 0x34, - 0x45, 0x7d, 0xe2, 0xe9, 0xf6, 0x9b, 0xd9, 0xcc, 0x78, 0xbf, 0xf9, 0xe6, 0xd3, 0xc1, 0x71, 0xbc, - 0x29, 0x50, 0x6f, 0x54, 0x32, 0xca, 0x74, 0x5a, 0xa4, 0xbc, 0x5f, 0xe3, 0xe8, 0xe7, 0x0e, 0x74, - 0x67, 0x69, 0xa9, 0x17, 0xc8, 0x8f, 0xa1, 0x33, 0x9d, 0x08, 0x36, 0x60, 0x43, 0x4f, 0x76, 0xa6, - 0x13, 0xce, 0xc1, 0x7f, 0xae, 0xd6, 0x28, 0x3a, 0x03, 0x36, 0x0c, 0xa5, 0x39, 0x53, 0xec, 0xa2, - 0xca, 0x50, 0x78, 0x36, 0x46, 0x67, 0xfe, 0x08, 0xfa, 0x97, 0x39, 0x55, 0x5b, 0xa3, 0xf0, 0x4d, - 0xbc, 0xc1, 0x94, 0x3b, 0x57, 0x79, 0xbe, 0x4d, 0xf5, 0x52, 0x04, 0x36, 0x57, 0x63, 0xfe, 0x2e, - 0x78, 0x97, 0xf2, 0x4c, 0x74, 0x4d, 0x98, 0x8e, 0x5c, 0x40, 0x6f, 0x82, 0x2b, 0x55, 0x26, 0x85, - 0xe8, 0x0d, 0xd8, 0xb0, 0x2f, 0x6b, 0x48, 0x75, 0x2e, 0x30, 0xc1, 0x6b, 0xad, 0x56, 0xa2, 0x6f, - 0xeb, 0xd4, 0x98, 0x8f, 0x80, 0x4f, 0x37, 0x39, 0x2e, 0x4a, 0x8d, 0xb3, 0x57, 0x71, 0x76, 0x85, - 0x3a, 0x5e, 0x55, 0x22, 0x34, 0x05, 0x0e, 0x64, 0xa8, 0xcb, 0x33, 0x2c, 0x14, 0xf5, 0x06, 0x53, - 0xaa, 0x86, 0xd1, 0x2f, 0x0c, 0xc2, 0x89, 0xca, 0x6f, 0xe6, 0xa9, 0xd2, 0xcb, 0xb7, 0xe2, 0xe3, - 0x33, 0x08, 0x16, 0x98, 0x24, 0xb9, 0xf0, 0x06, 0xde, 0xf0, 0xe8, 0xf1, 0xc3, 0x51, 0x43, 0x74, - 0x53, 0xe7, 0x04, 0x93, 0x44, 0xda, 0x5b, 0xfc, 0x0b, 0x08, 0x0b, 0x5c, 0x67, 0x89, 0x2a, 0x30, - 0x17, 0xbe, 0xf9, 0x09, 0x6f, 0x7f, 0x72, 0xe1, 0x52, 0xb2, 0xbd, 0x14, 0xfd, 0xd9, 0x81, 0xfb, - 0x7b, 0xa5, 0xf8, 0x3d, 0x60, 0x77, 0xe6, 0xab, 0x02, 0xc9, 0xee, 0x08, 0x55, 0xe6, 0x8b, 0x02, - 0xc9, 0x2a, 0x42, 0x5b, 0x33, 0x9b, 0x40, 0xb2, 0x2d, 0xa1, 0x1b, 0x33, 0x91, 0x40, 0xb2, 0x1b, - 0xfe, 0x09, 0xf4, 0x7e, 0x2a, 0x51, 0xc7, 0x98, 0x8b, 0xc0, 0x74, 0x7e, 0xa7, 0xed, 0xfc, 0xa2, - 0x44, 0x5d, 0xc9, 0x3a, 0x4f, 0x2f, 0x35, 0xd3, 0xb4, 0xa3, 0x31, 0x67, 0x8a, 0x15, 0x34, 0xf9, - 0x9e, 0x8d, 0xd1, 0xd9, 0x31, 0x64, 0xe7, 0x41, 0x0c, 0x7d, 0x09, 0xbe, 0xba, 0xc3, 0x5c, 0x84, - 0xa6, 0xfe, 0x87, 0x6f, 0x20, 0x63, 0x34, 0xbe, 0xc3, 0xfc, 0x9b, 0x4d, 0xa1, 0x2b, 0x69, 0xae, - 0x3f, 0x7a, 0x01, 0x61, 0x13, 0x22, 0x55, 0xbc, 0xc2, 0xca, 0x3c, 0x30, 0x94, 0x74, 0xe4, 0x23, - 0x08, 0x6e, 0x55, 0x52, 0x5a, 0xe2, 0x8f, 0x1e, 0x8b, 0x03, 0x65, 0xa5, 0xda, 0x5c, 0xa3, 0xb4, - 0xd7, 0xbe, 0xee, 0x7c, 0xc5, 0xa2, 0x21, 0x1c, 0xef, 0x27, 0xf9, 0x7b, 0xd0, 0x9d, 0xa7, 0xe5, - 0x66, 0x99, 0x0b, 0x36, 0xf0, 0x86, 0x81, 0x74, 0x28, 0xfa, 0x8b, 0x91, 0xb4, 0x2c, 0xdd, 0x3b, - 0x23, 0xb7, 0x0f, 0x7a, 0x1f, 0xfa, 0x34, 0x8a, 0x97, 0xb7, 0x4a, 0xbb, 0xb1, 0xf7, 0x08, 0x5f, - 0x29, 0xcd, 0x3f, 0x87, 0xae, 0x69, 0x77, 0x60, 0xf4, 0x75, 0xb9, 0x2b, 0xca, 0x4b, 0x77, 0xad, - 0x21, 0xd0, 0xdf, 0x21, 0xf0, 0x01, 0x04, 0x89, 0x9a, 0x63, 0xe2, 0x76, 0xc3, 0x02, 0x12, 0x15, - 0x4d, 0xa2, 0x32, 0xfc, 0x1f, 0xac, 0x6c, 0xe7, 0x65, 0x6f, 0x45, 0x97, 0x70, 0x7f, 0xaf, 0x63, - 0xd3, 0x89, 0xed, 0x77, 0x6a, 0x49, 0x0c, 0x1d, 0x55, 0xb4, 0x56, 0x39, 0x26, 0xb8, 0x28, 0x70, - 0x69, 0x64, 0xd3, 0x97, 0x0d, 0x8e, 0x7e, 0x63, 0x6d, 0x5d, 0xd3, 0x8f, 0x16, 0x67, 0x91, 0xae, - 0xd7, 0x6a, 0xb3, 0x74, 0xa5, 0x6b, 0x48, 0xbc, 0x2d, 0xe7, 0xae, 0x74, 0x67, 0x39, 0x27, 0xac, - 0x33, 0x67, 0x12, 0x1d, 0x9d, 0xf1, 0x01, 0x1c, 0xad, 0x51, 0xe5, 0xa5, 0xc6, 0x35, 0x6e, 0x0a, - 0x47, 0xc1, 0x6e, 0x88, 0x3f, 0x84, 0x5e, 0xa1, 0xae, 0x5f, 0xd2, 0xe8, 0x2d, 0x17, 0xdd, 0x42, - 0x5d, 0x9f, 0x62, 0xc5, 0x3f, 0x80, 0x70, 0x15, 0x63, 0xb2, 0x34, 0x29, 0x2b, 0xc8, 0xbe, 0x09, - 0x9c, 0x62, 0x15, 0xfd, 0xce, 0xa0, 0x3b, 0x43, 0x7d, 0x8b, 0xfa, 0xad, 0xb6, 0x75, 0xd7, 0xa9, - 0xbc, 0xff, 0x70, 0x2a, 0xff, 0xb0, 0x53, 0x05, 0xad, 0x53, 0x3d, 0x80, 0x60, 0xa6, 0x17, 0xd3, - 0x89, 0xf9, 0x22, 0x4f, 0x5a, 0x40, 0x1a, 0x1b, 0x2f, 0x8a, 0xf8, 0x16, 0x9d, 0x7d, 0x39, 0x14, - 0xfd, 0xca, 0xa0, 0x7b, 0xa6, 0xaa, 0xb4, 0x2c, 0x5e, 0x53, 0xd8, 0x00, 0x8e, 0xc6, 0x59, 0x96, - 0xc4, 0x0b, 0x55, 0xc4, 0xe9, 0xc6, 0x7d, 0xed, 0x6e, 0x88, 0x6e, 0x3c, 0xdb, 0xe1, 0xce, 0x7e, - 0xf7, 0x6e, 0x88, 0x7f, 0x04, 0xc1, 0x89, 0x31, 0x21, 0xeb, 0x28, 0xc7, 0xad, 0x5e, 0xac, 0xf7, - 0x98, 0x24, 0x3d, 0x70, 0x5c, 0x16, 0xe9, 0x2a, 0x49, 0xb7, 0xe6, 0x25, 0x7d, 0xd9, 0xe0, 0xe8, - 0x6f, 0x06, 0xfe, 0xff, 0x65, 0x2e, 0xf7, 0x80, 0xc5, 0x6e, 0x90, 0x2c, 0x6e, 0xac, 0xa6, 0xb7, - 0x63, 0x35, 0x02, 0x7a, 0x95, 0xa6, 0xa5, 0xcd, 0x45, 0x7f, 0xe0, 0x0d, 0x3d, 0x59, 0x43, 0x93, - 0x31, 0x3b, 0x62, 0x3d, 0x26, 0x94, 0x35, 0x6c, 0x34, 0x0f, 0xad, 0xe6, 0xa3, 0x3f, 0x18, 0x04, - 0x8d, 0x72, 0x4f, 0xf6, 0x95, 0x7b, 0xd2, 0x2a, 0x77, 0xf2, 0xb4, 0x56, 0xee, 0xe4, 0x29, 0x61, - 0x79, 0x5e, 0x2b, 0x57, 0x9e, 0x13, 0x6b, 0xdf, 0xea, 0xb4, 0xcc, 0x9e, 0x56, 0x96, 0xde, 0x50, - 0x36, 0x98, 0xc6, 0xfd, 0xc3, 0x0d, 0x6a, 0xf7, 0xe6, 0x50, 0x3a, 0x44, 0xe2, 0x38, 0x33, 0x5b, - 0x6d, 0x5f, 0x69, 0x01, 0xff, 0x18, 0x02, 0xe3, 0x44, 0xe6, 0xa9, 0x7b, 0x04, 0x39, 0xf7, 0x32, - 0x7f, 0xa2, 0x27, 0xee, 0x1a, 0x55, 0xb9, 0xcc, 0x32, 0xd4, 0x4e, 0xd3, 0x16, 0x98, 0xda, 0xe9, - 0x16, 0xad, 0x1d, 0x79, 0xd2, 0x82, 0xe8, 0x47, 0x08, 0xc7, 0x09, 0xea, 0x42, 0x96, 0xc9, 0xeb, - 0x26, 0xc6, 0xc1, 0xff, 0x6e, 0xf6, 0xfd, 0xf3, 0x7a, 0x13, 0xe8, 0xdc, 0xea, 0xd7, 0xfb, 0x97, - 0x7e, 0x4f, 0x55, 0xa6, 0xa6, 0x13, 0x33, 0x58, 0x4f, 0x3a, 0x14, 0x7d, 0x0a, 0x3e, 0xed, 0xc9, - 0x4e, 0x65, 0xff, 0x4d, 0x3b, 0x36, 0xef, 0x9a, 0xff, 0x30, 0x9e, 0xfc, 0x13, 0x00, 0x00, 0xff, - 0xff, 0xa5, 0xc4, 0x18, 0xb5, 0x73, 0x08, 0x00, 0x00, + // 935 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xbc, 0x56, 0xdd, 0x8e, 0xdb, 0x44, + 0x14, 0xd6, 0xc4, 0x76, 0x12, 0x9f, 0x6d, 0x17, 0x34, 0xaa, 0xa8, 0x29, 0x12, 0x0a, 0x16, 0x48, + 0x01, 0x89, 0x05, 0xb5, 0x42, 0x42, 0xdc, 0x65, 0x37, 0xa8, 0x0a, 0xbb, 0x2d, 0xcb, 0x64, 0x77, + 0xb9, 0x42, 0xd5, 0x24, 0x39, 0xd9, 0xb5, 0xea, 0xd8, 0x66, 0x6c, 0x6f, 0xe2, 0x57, 0xe0, 0x8a, + 0x27, 0x40, 0x42, 0xe2, 0x8a, 0x4b, 0x5e, 0x80, 0x87, 0xe0, 0x85, 0xd0, 0x99, 0x19, 0xff, 0x84, + 0x6e, 0x51, 0xaf, 0x7a, 0x37, 0xdf, 0x39, 0x93, 0xef, 0x78, 0xbe, 0xf3, 0x9d, 0xa3, 0xc0, 0x61, + 0x94, 0x14, 0xa8, 0x12, 0x19, 0x1f, 0x65, 0x2a, 0x2d, 0x52, 0x3e, 0xac, 0x71, 0xf8, 0x4b, 0x0f, + 0xfa, 0xf3, 0xb4, 0x54, 0x4b, 0xe4, 0x87, 0xd0, 0x9b, 0x4d, 0x03, 0x36, 0x62, 0x63, 0x47, 0xf4, + 0x66, 0x53, 0xce, 0xc1, 0x7d, 0x2e, 0x37, 0x18, 0xf4, 0x46, 0x6c, 0xec, 0x0b, 0x7d, 0xa6, 0xd8, + 0x45, 0x95, 0x61, 0xe0, 0x98, 0x18, 0x9d, 0xf9, 0x23, 0x18, 0x5e, 0xe6, 0xc4, 0xb6, 0xc1, 0xc0, + 0xd5, 0xf1, 0x06, 0x53, 0xee, 0x5c, 0xe6, 0xf9, 0x36, 0x55, 0xab, 0xc0, 0x33, 0xb9, 0x1a, 0xf3, + 0x77, 0xc1, 0xb9, 0x14, 0x67, 0x41, 0x5f, 0x87, 0xe9, 0xc8, 0x03, 0x18, 0x4c, 0x71, 0x2d, 0xcb, + 0xb8, 0x08, 0x06, 0x23, 0x36, 0x1e, 0x8a, 0x1a, 0x12, 0xcf, 0x05, 0xc6, 0x78, 0xad, 0xe4, 0x3a, + 0x18, 0x1a, 0x9e, 0x1a, 0xf3, 0x23, 0xe0, 0xb3, 0x24, 0xc7, 0x65, 0xa9, 0x70, 0xfe, 0x32, 0xca, + 0xae, 0x50, 0x45, 0xeb, 0x2a, 0xf0, 0x35, 0xc1, 0x1d, 0x19, 0xaa, 0xf2, 0x0c, 0x0b, 0x49, 0xb5, + 0x41, 0x53, 0xd5, 0x30, 0xfc, 0x95, 0x81, 0x3f, 0x95, 0xf9, 0xcd, 0x22, 0x95, 0x6a, 0xf5, 0x46, + 0x7a, 0x7c, 0x0e, 0xde, 0x12, 0xe3, 0x38, 0x0f, 0x9c, 0x91, 0x33, 0x3e, 0x78, 0xfc, 0xf0, 0xa8, + 0x11, 0xba, 0xe1, 0x39, 0xc1, 0x38, 0x16, 0xe6, 0x16, 0xff, 0x12, 0xfc, 0x02, 0x37, 0x59, 0x2c, + 0x0b, 0xcc, 0x03, 0x57, 0xff, 0x84, 0xb7, 0x3f, 0xb9, 0xb0, 0x29, 0xd1, 0x5e, 0x0a, 0xff, 0xec, + 0xc1, 0xfd, 0x3d, 0x2a, 0x7e, 0x0f, 0xd8, 0x4e, 0x7f, 0x95, 0x27, 0xd8, 0x8e, 0x50, 0xa5, 0xbf, + 0xc8, 0x13, 0xac, 0x22, 0xb4, 0xd5, 0xbd, 0xf1, 0x04, 0xdb, 0x12, 0xba, 0xd1, 0x1d, 0xf1, 0x04, + 0xbb, 0xe1, 0x9f, 0xc2, 0xe0, 0xe7, 0x12, 0x55, 0x84, 0x79, 0xe0, 0xe9, 0xca, 0xef, 0xb4, 0x95, + 0x7f, 0x28, 0x51, 0x55, 0xa2, 0xce, 0xd3, 0x4b, 0x75, 0x37, 0x4d, 0x6b, 0xf4, 0x99, 0x62, 0x05, + 0x75, 0x7e, 0x60, 0x62, 0x74, 0xb6, 0x0a, 0x99, 0x7e, 0x90, 0x42, 0x5f, 0x81, 0x2b, 0x77, 0x98, + 0x07, 0xbe, 0xe6, 0xff, 0xe8, 0x35, 0x62, 0x1c, 0x4d, 0x76, 0x98, 0x7f, 0x9b, 0x14, 0xaa, 0x12, + 0xfa, 0xfa, 0xa3, 0xa7, 0xe0, 0x37, 0x21, 0x72, 0xc5, 0x4b, 0xac, 0xf4, 0x03, 0x7d, 0x41, 0x47, + 0xfe, 0x31, 0x78, 0xb7, 0x32, 0x2e, 0x8d, 0xf0, 0x07, 0x8f, 0x0f, 0x5b, 0xda, 0xc9, 0x2e, 0xca, + 0x85, 0x49, 0x7e, 0xd3, 0xfb, 0x9a, 0x85, 0x1f, 0x82, 0x4b, 0x21, 0xfe, 0x1e, 0xf4, 0x17, 0x69, + 0x99, 0xac, 0xf2, 0x80, 0x8d, 0x9c, 0xb1, 0x23, 0x2c, 0x0a, 0xff, 0x66, 0x64, 0x23, 0x23, 0x6d, + 0xa7, 0xbd, 0xe6, 0xe3, 0xdf, 0x87, 0x21, 0xc9, 0xfe, 0xe2, 0x56, 0x2a, 0xdb, 0xe2, 0x01, 0xe1, + 0x2b, 0xa9, 0xf8, 0x17, 0xd0, 0xd7, 0x45, 0xee, 0x68, 0x73, 0x4d, 0x77, 0x45, 0x79, 0x61, 0xaf, + 0x35, 0x62, 0xb9, 0x1d, 0xb1, 0x1e, 0x80, 0x17, 0xcb, 0x05, 0xc6, 0x76, 0x0e, 0x0c, 0x20, 0x03, + 0x91, 0xea, 0x95, 0xd6, 0xfa, 0x4e, 0x66, 0xd3, 0x1b, 0x73, 0x2b, 0xbc, 0x84, 0xfb, 0x7b, 0x15, + 0x9b, 0x4a, 0x6c, 0xbf, 0x52, 0x2b, 0x98, 0x6f, 0x05, 0xa2, 0x11, 0xca, 0x31, 0xc6, 0x65, 0x81, + 0x2b, 0x6d, 0x91, 0xa1, 0x68, 0x70, 0xf8, 0x3b, 0x6b, 0x79, 0x75, 0x3d, 0x1a, 0x92, 0x65, 0xba, + 0xd9, 0xc8, 0x64, 0x65, 0xa9, 0x6b, 0x48, 0xba, 0xad, 0x16, 0x96, 0xba, 0xb7, 0x5a, 0x10, 0x56, + 0x99, 0x5d, 0x08, 0x3d, 0x95, 0xf1, 0x11, 0x1c, 0x6c, 0x50, 0xe6, 0xa5, 0xc2, 0x0d, 0x26, 0x85, + 0x95, 0xa0, 0x1b, 0xe2, 0x0f, 0x61, 0x50, 0xc8, 0xeb, 0x17, 0xd4, 0x66, 0xa3, 0x45, 0xbf, 0x90, + 0xd7, 0xa7, 0x58, 0xf1, 0x0f, 0xc0, 0x5f, 0x47, 0x18, 0xaf, 0x74, 0xca, 0x98, 0x6f, 0xa8, 0x03, + 0xa7, 0x58, 0x85, 0x7f, 0x30, 0xe8, 0xcf, 0x51, 0xdd, 0xa2, 0x7a, 0xa3, 0xc9, 0xec, 0x6e, 0x25, + 0xe7, 0x7f, 0xb6, 0x92, 0x7b, 0xf7, 0x56, 0xf2, 0xda, 0xad, 0xf4, 0x00, 0xbc, 0xb9, 0x5a, 0xce, + 0xa6, 0xfa, 0x8b, 0x1c, 0x61, 0x00, 0x79, 0x6c, 0xb2, 0x2c, 0xa2, 0x5b, 0xb4, 0xab, 0xca, 0xa2, + 0xf0, 0x37, 0x06, 0xfd, 0x33, 0x59, 0xa5, 0x65, 0xf1, 0x8a, 0xc3, 0x46, 0x70, 0x30, 0xc9, 0xb2, + 0x38, 0x5a, 0xca, 0x22, 0x4a, 0x13, 0xfb, 0xb5, 0xdd, 0x10, 0xdd, 0x78, 0xd6, 0xd1, 0xce, 0x7c, + 0x77, 0x37, 0x44, 0xc3, 0x70, 0xa2, 0x17, 0x8e, 0xd9, 0x1e, 0x9d, 0x61, 0x30, 0x7b, 0x46, 0x27, + 0xe9, 0x81, 0x93, 0xb2, 0x48, 0xd7, 0x71, 0xba, 0xd5, 0x2f, 0x19, 0x8a, 0x06, 0x87, 0xff, 0x30, + 0x70, 0xdf, 0xd6, 0x22, 0xb9, 0x07, 0x2c, 0xb2, 0x8d, 0x64, 0x51, 0xb3, 0x56, 0x06, 0x9d, 0xb5, + 0x12, 0xc0, 0xa0, 0x52, 0x32, 0xb9, 0xc6, 0x3c, 0x18, 0xea, 0x59, 0xad, 0xa1, 0xce, 0xe8, 0x19, + 0x31, 0xfb, 0xc4, 0x17, 0x35, 0x6c, 0x3c, 0x0f, 0xad, 0xe7, 0xc3, 0xbf, 0x18, 0x78, 0x8d, 0x73, + 0x4f, 0xf6, 0x9d, 0x7b, 0xd2, 0x3a, 0x77, 0x7a, 0x5c, 0x3b, 0x77, 0x7a, 0x4c, 0x58, 0x9c, 0xd7, + 0xce, 0x15, 0xe7, 0xa4, 0xda, 0x53, 0x95, 0x96, 0xd9, 0x71, 0x65, 0xe4, 0xf5, 0x45, 0x83, 0xa9, + 0xdd, 0x3f, 0xde, 0xa0, 0xb2, 0x6f, 0xf6, 0x85, 0x45, 0x64, 0x8e, 0x33, 0x3d, 0xd5, 0xe6, 0x95, + 0x06, 0xf0, 0x4f, 0xc0, 0x13, 0xf4, 0x0a, 0xfd, 0xd4, 0x3d, 0x81, 0x74, 0x58, 0x98, 0x6c, 0xf8, + 0xc4, 0x5e, 0x23, 0x96, 0xcb, 0x2c, 0x43, 0x65, 0x3d, 0x6d, 0x80, 0xe6, 0x4e, 0xb7, 0x68, 0xd6, + 0x91, 0x23, 0x0c, 0x08, 0x7f, 0x02, 0x7f, 0x12, 0xa3, 0x2a, 0x44, 0x19, 0xbf, 0xba, 0xc4, 0x38, + 0xb8, 0xdf, 0xcd, 0xbf, 0x7f, 0x5e, 0x4f, 0x02, 0x9d, 0x5b, 0xff, 0x3a, 0xff, 0xf1, 0xef, 0xa9, + 0xcc, 0xe4, 0x6c, 0xaa, 0x1b, 0xeb, 0x08, 0x8b, 0xc2, 0xcf, 0xc0, 0xa5, 0x39, 0xe9, 0x30, 0xbb, + 0xaf, 0x9b, 0xb1, 0x45, 0x5f, 0xff, 0x9b, 0x78, 0xf2, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa1, + 0xf4, 0x45, 0x04, 0x5f, 0x08, 0x00, 0x00, } diff --git a/bolt/internal/internal_test.go b/bolt/internal/internal_test.go index 77efbb9d9a..5c0a3bfd9e 100644 --- a/bolt/internal/internal_test.go +++ b/bolt/internal/internal_test.go @@ -4,6 +4,7 @@ import ( "reflect" "testing" + "github.com/google/go-cmp/cmp" "github.com/influxdata/chronograf" "github.com/influxdata/chronograf/bolt/internal" ) @@ -104,3 +105,45 @@ func TestMarshalLayout(t *testing.T) { t.Fatalf("source protobuf copy error: got %#v, expected %#v", vv, layout) } } + +func Test_MarshalDashboard(t *testing.T) { + dashboard := chronograf.Dashboard{ + ID: 1, + Cells: []chronograf.DashboardCell{ + { + ID: "9b5367de-c552-4322-a9e8-7f384cbd235c", + X: 0, + Y: 0, + W: 4, + H: 4, + Name: "Super awesome query", + Queries: []chronograf.DashboardQuery{ + { + Command: "select * from cpu", + Label: "CPU Utilization", + Range: &chronograf.Range{ + Upper: int64(100), + }, + }, + }, + Axes: map[string]chronograf.Axis{ + "y": chronograf.Axis{ + Bounds: [2]int64{0, 100}, + }, + }, + Type: "line", + }, + }, + Templates: []chronograf.Template{}, + Name: "Dashboard", + } + + var actual chronograf.Dashboard + if buf, err := internal.MarshalDashboard(dashboard); err != nil { + t.Fatal("Error marshaling dashboard: err", err) + } else if err := internal.UnmarshalDashboard(buf, &actual); err != nil { + t.Fatal("Error unmarshaling dashboard: err:", err) + } else if !cmp.Equal(dashboard, actual) { + t.Fatalf("Dashboard protobuf copy error: diff follows:\n%s", cmp.Diff(dashboard, actual)) + } +} From ee6a2fa54b8619d84b9a30ca54f14f9ff05413c7 Mon Sep 17 00:00:00 2001 From: Tim Raymond Date: Fri, 21 Jul 2017 12:09:49 -0400 Subject: [PATCH 05/93] Enforce only "x", "y", and "y2" axes For the forseeable future, we will only be using the "x", "y", and "y2" axes, even though the underlying serialization can support arbitrary axes (for the future). This ensures that only "x", "y", and "y2" axes are present and updates the Swagger docs to reflect that fact --- chronograf.go | 1 + server/cells.go | 16 ++++++++++- server/cells_test.go | 60 +++++++++++++++++++++++++++++++++++++++ server/dashboards_test.go | 21 ++++++++++++-- server/swagger.json | 54 ++++++++++++++++++++--------------- 5 files changed, 126 insertions(+), 26 deletions(-) create mode 100644 server/cells_test.go diff --git a/chronograf.go b/chronograf.go index c1401790dc..8a62763ab3 100644 --- a/chronograf.go +++ b/chronograf.go @@ -29,6 +29,7 @@ const ( ErrAlertNotFound = Error("alert not found") ErrAuthentication = Error("user not authenticated") ErrUninitialized = Error("client uninitialized. Call Open() method") + ErrInvalidAxis = Error("Unexpected axis in cell. Valid axes are 'x', 'y', and 'y2'") ) // Error is a domain error encountered while processing chronograf requests diff --git a/server/cells.go b/server/cells.go index b6a79290b6..f60437c910 100644 --- a/server/cells.go +++ b/server/cells.go @@ -43,9 +43,23 @@ func newCellResponses(dID chronograf.DashboardID, dcells []chronograf.DashboardC return cells } -// ValidDashboardCellRequest verifies that the dashboard cells have a query +// ValidDashboardCellRequest verifies that the dashboard cells have a query and +// have the correct axes specified func ValidDashboardCellRequest(c *chronograf.DashboardCell) error { CorrectWidthHeight(c) + return HasCorrectAxes(c) +} + +// HasCorrectAxes verifies that only permitted axes exist within a DashboardCell +func HasCorrectAxes(c *chronograf.DashboardCell) error { + for axis, _ := range c.Axes { + switch axis { + case "x", "y", "y2": + // no-op + default: + return chronograf.ErrInvalidAxis + } + } return nil } diff --git a/server/cells_test.go b/server/cells_test.go new file mode 100644 index 0000000000..be694b3dff --- /dev/null +++ b/server/cells_test.go @@ -0,0 +1,60 @@ +package server_test + +import ( + "testing" + + "github.com/influxdata/chronograf" + "github.com/influxdata/chronograf/server" +) + +func Test_Cells_CorrectAxis(t *testing.T) { + t.Parallel() + + axisTests := []struct { + name string + cell *chronograf.DashboardCell + shouldFail bool + }{ + { + "correct axes", + &chronograf.DashboardCell{ + Axes: map[string]chronograf.Axis{ + "x": chronograf.Axis{ + Bounds: [2]int64{0, 100}, + }, + "y": chronograf.Axis{ + Bounds: [2]int64{0, 100}, + }, + "y2": chronograf.Axis{ + Bounds: [2]int64{0, 100}, + }, + }, + }, + false, + }, + { + "invalid axes present", + &chronograf.DashboardCell{ + Axes: map[string]chronograf.Axis{ + "axis of evil": chronograf.Axis{ + Bounds: [2]int64{666, 666}, + }, + "axis of awesome": chronograf.Axis{ + Bounds: [2]int64{1337, 31337}, + }, + }, + }, + true, + }, + } + + for _, test := range axisTests { + t.Run(test.name, func(tt *testing.T) { + if err := server.HasCorrectAxes(test.cell); err != nil && !test.shouldFail { + t.Errorf("%q: Unexpected error: err: %s", test.name, err) + } else if err == nil && test.shouldFail { + t.Errorf("%q: Expected error and received none", test.name) + } + }) + } +} diff --git a/server/dashboards_test.go b/server/dashboards_test.go index 613d56d574..b5116913c5 100644 --- a/server/dashboards_test.go +++ b/server/dashboards_test.go @@ -4,6 +4,7 @@ import ( "reflect" "testing" + "github.com/google/go-cmp/cmp" "github.com/influxdata/chronograf" ) @@ -219,6 +220,14 @@ func Test_newDashboardResponse(t *testing.T) { Command: "SELECT donors from hill_valley_preservation_society where time > '1985-10-25 08:00:00'", }, }, + Axes: map[string]chronograf.Axis{ + "x": chronograf.Axis{ + Bounds: [2]int64{0, 100}, + }, + "y": chronograf.Axis{ + Bounds: [2]int64{2, 95}, + }, + }, }, { ID: "b", @@ -257,6 +266,14 @@ func Test_newDashboardResponse(t *testing.T) { }, }, }, + Axes: map[string]chronograf.Axis{ + "x": chronograf.Axis{ + Bounds: [2]int64{0, 100}, + }, + "y": chronograf.Axis{ + Bounds: [2]int64{2, 95}, + }, + }, }, }, dashboardCellResponse{ @@ -301,8 +318,8 @@ func Test_newDashboardResponse(t *testing.T) { }, } for _, tt := range tests { - if got := newDashboardResponse(tt.d); !reflect.DeepEqual(got, tt.want) { - t.Errorf("%q. newDashboardResponse() = \n%#v\n\n, want\n\n%#v", tt.name, got, tt.want) + if got := newDashboardResponse(tt.d); !cmp.Equal(got, tt.want) { + t.Errorf("%q. newDashboardResponse() = diff:\n%s", tt.name, cmp.Diff(got, tt.want)) } } } diff --git a/server/swagger.json b/server/swagger.json index 524e5c8192..1891640b8d 100644 --- a/server/swagger.json +++ b/server/swagger.json @@ -3999,13 +3999,21 @@ "$ref": "#/definitions/DashboardQuery" } }, - "axes": { - "description": "The viewport for a cell's visualizations", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/DashboardRange" - } - }, + "axes": { + "description": "The viewport for a cell's visualizations", + "type": "object", + "properties": { + "x": { + "$ref": "#/definitions/DashboardRange" + }, + "y": { + "$ref": "#/definitions/DashboardRange" + }, + "y2": { + "$ref": "#/definitions/DashboardRange" + } + } + }, "type": { "description": "Cell visualization type", "type": "string", @@ -4087,22 +4095,22 @@ } } }, - "DashboardRange": { - "type": "object", - "description": "A description of a particular axis for a visualization", - "properties": { - "bounds": { - "type": "array", - "minItems": 0, - "maxItems": 2, - "description": "The extents of an axis in the form [lower, upper]. Clients determine whether bounds are to be inclusive or exclusive of their limits", - "items": { - "type": "integer", - "format": "int32" - } - } - } - }, + "DashboardRange": { + "type": "object", + "description": "A description of a particular axis for a visualization", + "properties": { + "bounds": { + "type": "array", + "minItems": 0, + "maxItems": 2, + "description": "The extents of an axis in the form [lower, upper]. Clients determine whether bounds are to be inclusive or exclusive of their limits", + "items": { + "type": "integer", + "format": "int64" + } + } + } + }, "Routes": { "type": "object", "properties": { From f3c1eb1649c50eb61749bb1d9f5d43c0da03c97e Mon Sep 17 00:00:00 2001 From: Hunter Trujillo Date: Thu, 6 Jul 2017 12:16:31 -0600 Subject: [PATCH 06/93] Refactor Overlay Controls into a separate component for later. Add Display Options component. Replace Overlay Controls with Display Options. --- .../components/CellEditorOverlay.js | 39 ++++++---- .../dashboards/components/DisplayOptions.js | 8 ++ .../components/GraphTypeSelector.js | 34 ++++++++ .../dashboards/components/OverlayControls.js | 78 +++++++++---------- 4 files changed, 106 insertions(+), 53 deletions(-) create mode 100644 ui/src/dashboards/components/DisplayOptions.js create mode 100644 ui/src/dashboards/components/GraphTypeSelector.js diff --git a/ui/src/dashboards/components/CellEditorOverlay.js b/ui/src/dashboards/components/CellEditorOverlay.js index 791ab72523..a82e46e917 100644 --- a/ui/src/dashboards/components/CellEditorOverlay.js +++ b/ui/src/dashboards/components/CellEditorOverlay.js @@ -7,6 +7,7 @@ import ResizeContainer from 'shared/components/ResizeContainer' import QueryMaker from 'src/data_explorer/components/QueryMaker' import Visualization from 'src/data_explorer/components/Visualization' import OverlayControls from 'src/dashboards/components/OverlayControls' +import DisplayOptions from 'src/dashboards/components/DisplayOptions' import * as queryModifiers from 'src/utils/queryTransitions' import defaultQueryConfig from 'src/utils/defaultQueryConfig' @@ -28,7 +29,7 @@ class CellEditorOverlay extends Component { this.handleSaveCell = ::this.handleSaveCell - this.handleSelectGraphType = ::this.handleSelectGraphType + this.handleSelectDisplayOptions = ::this.handleSelectDisplayOptions this.handleSetActiveQueryIndex = ::this.handleSetActiveQueryIndex this.handleEditRawText = ::this.handleEditRawText @@ -43,6 +44,7 @@ class CellEditorOverlay extends Component { cellWorkingType: type, queriesWorkingDraft, activeQueryIndex: 0, + isDisplayOptionsTabOpen: false, } } @@ -112,6 +114,12 @@ class CellEditorOverlay extends Component { this.setState({cellWorkingType: graphType}) } + handleSelectDisplayOptions(isDisplayOptionsTabOpen) { + return () => { + this.setState({isDisplayOptionsTabOpen}) + } + } + handleSetActiveQueryIndex(activeQueryIndex) { this.setState({activeQueryIndex}) } @@ -146,6 +154,7 @@ class CellEditorOverlay extends Component { activeQueryIndex, cellWorkingName, cellWorkingType, + isDisplayOptionsTabOpen, queriesWorkingDraft, } = this.state @@ -181,23 +190,25 @@ class CellEditorOverlay extends Component { />
- + {isDisplayOptionsTabOpen + ? + : }
diff --git a/ui/src/dashboards/components/DisplayOptions.js b/ui/src/dashboards/components/DisplayOptions.js new file mode 100644 index 0000000000..0f255f78a3 --- /dev/null +++ b/ui/src/dashboards/components/DisplayOptions.js @@ -0,0 +1,8 @@ +import React, {PropTypes} from 'react' + +const DisplayOptions = () => +
Display Options
+ +DisplayOptions.propTypes = {} + +export default DisplayOptions diff --git a/ui/src/dashboards/components/GraphTypeSelector.js b/ui/src/dashboards/components/GraphTypeSelector.js new file mode 100644 index 0000000000..9ce9c34478 --- /dev/null +++ b/ui/src/dashboards/components/GraphTypeSelector.js @@ -0,0 +1,34 @@ +import React, {PropTypes} from 'react' +import classnames from 'classnames' + +import graphTypes from 'hson!shared/data/graphTypes.hson' + +const GraphTypeSelector = ({selectedGraphType, onSelectGraphType}) => +
+

Cell Editor

+
+

Visualization Type

+
    + {graphTypes.map(graphType => +
  • onSelectGraphType(graphType.type)} + > + {graphType.menuOption} +
  • + )} +
+
+
+ +const {func, string} = PropTypes + +GraphTypeSelector.propTypes = { + selectedGraphType: string.isRequired, + onSelectGraphType: func.isRequired, +} + +export default GraphTypeSelector diff --git a/ui/src/dashboards/components/OverlayControls.js b/ui/src/dashboards/components/OverlayControls.js index a13a29f72e..e87a6a28db 100644 --- a/ui/src/dashboards/components/OverlayControls.js +++ b/ui/src/dashboards/components/OverlayControls.js @@ -3,51 +3,51 @@ import classnames from 'classnames' import ConfirmButtons from 'shared/components/ConfirmButtons' -import graphTypes from 'hson!shared/data/graphTypes.hson' - -const OverlayControls = props => { - const { - onCancel, - onSave, - selectedGraphType, - onSelectGraphType, - isSavable, - } = props - return ( -
-

Cell Editor

-
-

Visualization Type:

-
    - {graphTypes.map(graphType => -
  • onSelectGraphType(graphType.type)} - > - {graphType.menuOption} -
  • - )} -
- -
+const OverlayControls = ({ + onCancel, + onSave, + isDisplayOptionsTabOpen, + onSelectDisplayOptions, + isSavable, +}) => +
+

Cell Editor

+
+
    +
  • + Queries +
  • +
  • + Display Options +
  • +
+
- ) -} +
-const {func, string, bool} = PropTypes +const {func, bool} = PropTypes OverlayControls.propTypes = { onCancel: func.isRequired, onSave: func.isRequired, - selectedGraphType: string.isRequired, - onSelectGraphType: func.isRequired, + isDisplayOptionsTabOpen: bool.isRequired, + onSelectDisplayOptions: func.isRequired, isSavable: bool, } From 64ba00804f6f0388fce85ed1c224620be4a23870 Mon Sep 17 00:00:00 2001 From: Hunter Trujillo Date: Thu, 6 Jul 2017 12:27:02 -0600 Subject: [PATCH 07/93] Fix visualization selection functionality. --- .../dashboards/components/CellEditorOverlay.js | 6 +++++- ui/src/dashboards/components/DisplayOptions.js | 18 +++++++++++++++--- .../dashboards/components/GraphTypeSelector.js | 1 - 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/ui/src/dashboards/components/CellEditorOverlay.js b/ui/src/dashboards/components/CellEditorOverlay.js index a82e46e917..7e36b71a9b 100644 --- a/ui/src/dashboards/components/CellEditorOverlay.js +++ b/ui/src/dashboards/components/CellEditorOverlay.js @@ -29,6 +29,7 @@ class CellEditorOverlay extends Component { this.handleSaveCell = ::this.handleSaveCell + this.handleSelectGraphType = ::this.handleSelectGraphType this.handleSelectDisplayOptions = ::this.handleSelectDisplayOptions this.handleSetActiveQueryIndex = ::this.handleSetActiveQueryIndex this.handleEditRawText = ::this.handleEditRawText @@ -197,7 +198,10 @@ class CellEditorOverlay extends Component { isSavable={queriesWorkingDraft.every(isQuerySavable)} /> {isDisplayOptionsTabOpen - ? + ? : -
Display Options
+import GraphTypeSelector from 'src/dashboards/components/GraphTypeSelector' -DisplayOptions.propTypes = {} +const DisplayOptions = ({selectedGraphType, onSelectGraphType}) => +
+ +
+ +const {func, string} = PropTypes + +DisplayOptions.propTypes = { + selectedGraphType: string.isRequired, + onSelectGraphType: func.isRequired, +} export default DisplayOptions diff --git a/ui/src/dashboards/components/GraphTypeSelector.js b/ui/src/dashboards/components/GraphTypeSelector.js index 9ce9c34478..41406082dc 100644 --- a/ui/src/dashboards/components/GraphTypeSelector.js +++ b/ui/src/dashboards/components/GraphTypeSelector.js @@ -5,7 +5,6 @@ import graphTypes from 'hson!shared/data/graphTypes.hson' const GraphTypeSelector = ({selectedGraphType, onSelectGraphType}) =>
-

Cell Editor

Visualization Type

    From 3ffe5994000492ee23fe560d376002d24277cfe2 Mon Sep 17 00:00:00 2001 From: Hunter Trujillo Date: Thu, 6 Jul 2017 12:53:57 -0600 Subject: [PATCH 08/93] Add Ranger component. Add yRange prop. Add min / max inputs. --- .../components/CellEditorOverlay.js | 20 +++++++++++ .../dashboards/components/DisplayOptions.js | 16 +++++++-- ui/src/dashboards/components/Ranger.js | 35 +++++++++++++++++++ 3 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 ui/src/dashboards/components/Ranger.js diff --git a/ui/src/dashboards/components/CellEditorOverlay.js b/ui/src/dashboards/components/CellEditorOverlay.js index 7e36b71a9b..fefbf4c181 100644 --- a/ui/src/dashboards/components/CellEditorOverlay.js +++ b/ui/src/dashboards/components/CellEditorOverlay.js @@ -33,6 +33,7 @@ class CellEditorOverlay extends Component { this.handleSelectDisplayOptions = ::this.handleSelectDisplayOptions this.handleSetActiveQueryIndex = ::this.handleSetActiveQueryIndex this.handleEditRawText = ::this.handleEditRawText + this.handleSetRange = ::this.handleSetRange const {cell: {name, type, queries}} = props @@ -46,6 +47,10 @@ class CellEditorOverlay extends Component { queriesWorkingDraft, activeQueryIndex: 0, isDisplayOptionsTabOpen: false, + yRange: { + min: 'auto', + max: 'auto', + }, } } @@ -121,6 +126,17 @@ class CellEditorOverlay extends Component { } } + handleSetRange(e) { + const {min, max} = e.target.form + this.setState({ + yRange: { + min: min.value, + max: max.value, + }, + }) + e.preventDefault() + } + handleSetActiveQueryIndex(activeQueryIndex) { this.setState({activeQueryIndex}) } @@ -157,6 +173,7 @@ class CellEditorOverlay extends Component { cellWorkingType, isDisplayOptionsTabOpen, queriesWorkingDraft, + yRange, } = this.state const queryActions = { @@ -187,6 +204,7 @@ class CellEditorOverlay extends Component { cellType={cellWorkingType} cellName={cellWorkingName} editQueryStatus={editQueryStatus} + yRange={yRange} views={[]} />
    @@ -201,6 +219,8 @@ class CellEditorOverlay extends Component { ? : +const DisplayOptions = ({ + selectedGraphType, + onSelectGraphType, + onSetRange, + yRange, +}) =>
    +
    -const {func, string} = PropTypes +const {func, shape, string} = PropTypes DisplayOptions.propTypes = { selectedGraphType: string.isRequired, onSelectGraphType: func.isRequired, + onSetRange: func.isRequired, + yRange: shape({ + min: string, + max: string, + }).isRequired, } export default DisplayOptions diff --git a/ui/src/dashboards/components/Ranger.js b/ui/src/dashboards/components/Ranger.js new file mode 100644 index 0000000000..808544ba15 --- /dev/null +++ b/ui/src/dashboards/components/Ranger.js @@ -0,0 +1,35 @@ +import React, {PropTypes} from 'react' + +const Ranger = ({onSetRange, yRange}) => +
    +
    + + + + +
    +
    + +const {func, shape, string} = PropTypes + +Ranger.propTypes = { + onSetRange: func.isRequired, + yRange: shape({ + min: string, + max: string, + }).isRequired, +} + +export default Ranger From 0ba94497e03dca3562cdd093628b4ed1e4dc4ea7 Mon Sep 17 00:00:00 2001 From: Hunter Trujillo Date: Thu, 6 Jul 2017 14:10:26 -0600 Subject: [PATCH 09/93] Ranger improvements. --- ui/src/dashboards/components/Ranger.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ui/src/dashboards/components/Ranger.js b/ui/src/dashboards/components/Ranger.js index 808544ba15..b96306181c 100644 --- a/ui/src/dashboards/components/Ranger.js +++ b/ui/src/dashboards/components/Ranger.js @@ -2,17 +2,19 @@ import React, {PropTypes} from 'react' const Ranger = ({onSetRange, yRange}) =>
    -
    - + + - + Date: Fri, 7 Jul 2017 09:37:42 -0700 Subject: [PATCH 10/93] Add reducer for cell range --- ui/spec/dashboards/reducers/uiSpec.js | 30 ++++++++++++++++----------- ui/src/dashboards/actions/index.js | 9 ++++++++ ui/src/dashboards/reducers/ui.js | 24 +++++++++++++++++++++ 3 files changed, 51 insertions(+), 12 deletions(-) diff --git a/ui/spec/dashboards/reducers/uiSpec.js b/ui/spec/dashboards/reducers/uiSpec.js index 9fc364ff00..e3cb3a9428 100644 --- a/ui/spec/dashboards/reducers/uiSpec.js +++ b/ui/spec/dashboards/reducers/uiSpec.js @@ -11,6 +11,7 @@ import { renameDashboardCell, syncDashboardCell, templateVariableSelected, + editCellRanges, } from 'src/dashboards/actions' let state @@ -59,22 +60,11 @@ const c1 = { w: 4, h: 4, id: 1, + i: 'im-a-cell-id-index', isEditing: false, name: 'Gigawatts', } const cells = [c1] -const tempVar = { - ...d1.templates[0], - id: '1', - type: 'measurement', - label: 'test query', - tempVar: '$HOSTS', - query: { - db: 'db1', - text: 'SHOW TAGS WHERE HUNTER = "coo"', - }, - values: ['h1', 'h2', 'h3'], -} describe('DataExplorer.Reducers.UI', () => { it('can load the dashboards', () => { @@ -180,4 +170,20 @@ describe('DataExplorer.Reducers.UI', () => { expect(actual.dashboards[0].templates[0].values[1].selected).to.equal(false) expect(actual.dashboards[0].templates[0].values[2].selected).to.equal(true) }) + + it('an set the range', () => { + const dash = {..._.cloneDeep(d1), cells: [c1]} + + state = { + dashboards: [dash], + } + + const y = [1, 2] + const y2 = [null, null] + + const actual = reducer(state, editCellRanges(dash, c1, {y, y2})) + + expect(actual.dashboards[0].cells[0].ranges.y).to.deep.equal(y) + expect(actual.dashboards[0].cells[0].ranges.y2).to.deep.equal(y2) + }) }) diff --git a/ui/src/dashboards/actions/index.js b/ui/src/dashboards/actions/index.js index 1ee23908f2..63ded813f4 100644 --- a/ui/src/dashboards/actions/index.js +++ b/ui/src/dashboards/actions/index.js @@ -79,6 +79,15 @@ export const addDashboardCell = (dashboard, cell) => ({ }, }) +export const editCellRanges = (dashboard, cell, ranges) => ({ + type: 'EDIT_CELL_RANGES', + payload: { + dashboard, + cell, + ranges, + }, +}) + export const editDashboardCell = (dashboard, x, y, isEditing) => ({ type: 'EDIT_DASHBOARD_CELL', // x and y coords are used as a alternative to cell ids, which are not diff --git a/ui/src/dashboards/reducers/ui.js b/ui/src/dashboards/reducers/ui.js index 92ae13c688..810e825182 100644 --- a/ui/src/dashboards/reducers/ui.js +++ b/ui/src/dashboards/reducers/ui.js @@ -239,6 +239,30 @@ export default function ui(state = initialState, action) { return {...state, dashboards} } + + case 'EDIT_CELL_RANGES': { + const {dashboard, cell, ranges} = action.payload + + const dashboards = state.dashboards.map( + d => + (d.id === dashboard.id + ? { + ...d, + cells: d.cells.map( + c => + (c.i === cell.i + ? { + ...c, + ranges, + } + : c) + ), + } + : d) + ) + + return {...state, dashboards} + } } return state From 30ee24fe0917cc9d72763bf7af9300ef60e347e9 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Mon, 10 Jul 2017 11:16:55 -0700 Subject: [PATCH 11/93] Add temporary styles --- ui/src/dashboards/components/DisplayOptions.js | 13 +++++++++++-- ui/src/dashboards/components/GraphTypeSelector.js | 11 ++++++----- ui/src/dashboards/components/OverlayControls.js | 3 ++- ui/src/dashboards/components/Ranger.js | 5 +++-- 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/ui/src/dashboards/components/DisplayOptions.js b/ui/src/dashboards/components/DisplayOptions.js index ba92f91db7..f34f429576 100644 --- a/ui/src/dashboards/components/DisplayOptions.js +++ b/ui/src/dashboards/components/DisplayOptions.js @@ -3,19 +3,28 @@ import React, {PropTypes} from 'react' import GraphTypeSelector from 'src/dashboards/components/GraphTypeSelector' import Ranger from 'src/dashboards/components/Ranger' +const style = { + height: '100%', + margin: '0 60px', + display: 'flex', + backgroundColor: '#202028', + justifyContent: 'space-around', +} + const DisplayOptions = ({ selectedGraphType, onSelectGraphType, onSetRange, yRange, -}) => -
    +}) => ( +
    +) const {func, shape, string} = PropTypes diff --git a/ui/src/dashboards/components/GraphTypeSelector.js b/ui/src/dashboards/components/GraphTypeSelector.js index 41406082dc..e57640e093 100644 --- a/ui/src/dashboards/components/GraphTypeSelector.js +++ b/ui/src/dashboards/components/GraphTypeSelector.js @@ -3,12 +3,12 @@ import classnames from 'classnames' import graphTypes from 'hson!shared/data/graphTypes.hson' -const GraphTypeSelector = ({selectedGraphType, onSelectGraphType}) => -
    -
    +const GraphTypeSelector = ({selectedGraphType, onSelectGraphType}) => ( +
    +

    Visualization Type

      - {graphTypes.map(graphType => + {graphTypes.map(graphType => (
    • > {graphType.menuOption}
    • - )} + ))}
    +) const {func, string} = PropTypes diff --git a/ui/src/dashboards/components/OverlayControls.js b/ui/src/dashboards/components/OverlayControls.js index e87a6a28db..8254f28e9e 100644 --- a/ui/src/dashboards/components/OverlayControls.js +++ b/ui/src/dashboards/components/OverlayControls.js @@ -9,7 +9,7 @@ const OverlayControls = ({ isDisplayOptionsTabOpen, onSelectDisplayOptions, isSavable, -}) => +}) => (

    Cell Editor

    @@ -40,6 +40,7 @@ const OverlayControls = ({ />
    +) const {func, bool} = PropTypes diff --git a/ui/src/dashboards/components/Ranger.js b/ui/src/dashboards/components/Ranger.js index b96306181c..6f50df0e80 100644 --- a/ui/src/dashboards/components/Ranger.js +++ b/ui/src/dashboards/components/Ranger.js @@ -1,7 +1,7 @@ import React, {PropTypes} from 'react' -const Ranger = ({onSetRange, yRange}) => -
    +const Ranger = ({onSetRange, yRange}) => ( +
    />
    +) const {func, shape, string} = PropTypes From ee7ae66a23d3122e3146b03e482663231ba2c1e9 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Mon, 10 Jul 2017 13:19:30 -0700 Subject: [PATCH 12/93] Hook up ranges to CEO --- ui/spec/dashboards/reducers/uiSpec.js | 6 +-- ui/src/dashboards/actions/index.js | 8 ++-- .../components/CellEditorOverlay.js | 46 +++++++++++-------- .../dashboards/components/DisplayOptions.js | 18 +++++--- ui/src/dashboards/components/Ranger.js | 22 +++++---- ui/src/dashboards/containers/DashboardPage.js | 7 ++- ui/src/dashboards/reducers/ui.js | 8 ++-- 7 files changed, 70 insertions(+), 45 deletions(-) diff --git a/ui/spec/dashboards/reducers/uiSpec.js b/ui/spec/dashboards/reducers/uiSpec.js index e3cb3a9428..5d21fb829b 100644 --- a/ui/spec/dashboards/reducers/uiSpec.js +++ b/ui/spec/dashboards/reducers/uiSpec.js @@ -181,9 +181,9 @@ describe('DataExplorer.Reducers.UI', () => { const y = [1, 2] const y2 = [null, null] - const actual = reducer(state, editCellRanges(dash, c1, {y, y2})) + const actual = reducer(state, editCellRanges(dash.id, c1.i, {y, y2})) - expect(actual.dashboards[0].cells[0].ranges.y).to.deep.equal(y) - expect(actual.dashboards[0].cells[0].ranges.y2).to.deep.equal(y2) + expect(actual.dashboards[0].cells[0].yRanges.y).to.deep.equal(y) + expect(actual.dashboards[0].cells[0].yRanges.y2).to.deep.equal(y2) }) }) diff --git a/ui/src/dashboards/actions/index.js b/ui/src/dashboards/actions/index.js index 63ded813f4..2a4841759e 100644 --- a/ui/src/dashboards/actions/index.js +++ b/ui/src/dashboards/actions/index.js @@ -79,12 +79,12 @@ export const addDashboardCell = (dashboard, cell) => ({ }, }) -export const editCellRanges = (dashboard, cell, ranges) => ({ +export const editCellRanges = (dashboardID, cellID, yRanges) => ({ type: 'EDIT_CELL_RANGES', payload: { - dashboard, - cell, - ranges, + dashboardID, + cellID, + yRanges, }, }) diff --git a/ui/src/dashboards/components/CellEditorOverlay.js b/ui/src/dashboards/components/CellEditorOverlay.js index fefbf4c181..7ef54ddfc8 100644 --- a/ui/src/dashboards/components/CellEditorOverlay.js +++ b/ui/src/dashboards/components/CellEditorOverlay.js @@ -28,6 +28,7 @@ class CellEditorOverlay extends Component { this.handleDeleteQuery = ::this.handleDeleteQuery this.handleSaveCell = ::this.handleSaveCell + this.handleEditCellRanges = ::this.handleEditCellRanges this.handleSelectGraphType = ::this.handleSelectGraphType this.handleSelectDisplayOptions = ::this.handleSelectDisplayOptions @@ -35,7 +36,7 @@ class CellEditorOverlay extends Component { this.handleEditRawText = ::this.handleEditRawText this.handleSetRange = ::this.handleSetRange - const {cell: {name, type, queries}} = props + const {cell: {name, type, queries, yRanges}} = props const queriesWorkingDraft = _.cloneDeep( queries.map(({queryConfig}) => ({...queryConfig, id: uuid.v4()})) @@ -47,9 +48,8 @@ class CellEditorOverlay extends Component { queriesWorkingDraft, activeQueryIndex: 0, isDisplayOptionsTabOpen: false, - yRange: { - min: 'auto', - max: 'auto', + yRanges: { + y: yRanges && yRanges.y ? yRanges.y : ['', ''], }, } } @@ -81,6 +81,24 @@ class CellEditorOverlay extends Component { } } + handleEditCellRanges(e) { + e.preventDefault() + const {dashboardID, cell: {i}} = this.props + const {yRanges} = this.state + + this.props.onEditCellRanges(+dashboardID, i, yRanges) + } + + handleSetRange(e) { + const {min, max} = e.target.form + this.setState({ + yRanges: { + y: [min.value, max.value], + }, + }) + e.preventDefault() + } + handleAddQuery(options) { const newQuery = Object.assign({}, defaultQueryConfig(uuid.v4()), options) const nextQueries = this.state.queriesWorkingDraft.concat(newQuery) @@ -126,17 +144,6 @@ class CellEditorOverlay extends Component { } } - handleSetRange(e) { - const {min, max} = e.target.form - this.setState({ - yRange: { - min: min.value, - max: max.value, - }, - }) - e.preventDefault() - } - handleSetActiveQueryIndex(activeQueryIndex) { this.setState({activeQueryIndex}) } @@ -173,7 +180,7 @@ class CellEditorOverlay extends Component { cellWorkingType, isDisplayOptionsTabOpen, queriesWorkingDraft, - yRange, + yRanges, } = this.state const queryActions = { @@ -204,7 +211,7 @@ class CellEditorOverlay extends Component { cellType={cellWorkingType} cellName={cellWorkingName} editQueryStatus={editQueryStatus} - yRange={yRange} + yRanges={yRanges} views={[]} />
    @@ -217,10 +224,11 @@ class CellEditorOverlay extends Component { /> {isDisplayOptionsTabOpen ? : (
    - +
    ) -const {func, shape, string} = PropTypes +const {array, func, shape, string} = PropTypes DisplayOptions.propTypes = { + onEditCellRanges: func.isRequired, selectedGraphType: string.isRequired, onSelectGraphType: func.isRequired, onSetRange: func.isRequired, - yRange: shape({ - min: string, - max: string, + yRanges: shape({ + y: array, + y2: array, }).isRequired, } diff --git a/ui/src/dashboards/components/Ranger.js b/ui/src/dashboards/components/Ranger.js index 6f50df0e80..349ab05acb 100644 --- a/ui/src/dashboards/components/Ranger.js +++ b/ui/src/dashboards/components/Ranger.js @@ -1,16 +1,17 @@ import React, {PropTypes} from 'react' -const Ranger = ({onSetRange, yRange}) => ( +const Ranger = ({onSetRange, yRanges, onEditCellRanges}) => (
    -
    + ( type="text" name="max" id="max" - value={yRange.max} + value={yRanges.y[1]} onChange={onSetRange} + placeholder="auto" /> +
    ) -const {func, shape, string} = PropTypes +const {array, func, shape} = PropTypes Ranger.propTypes = { + onEditCellRanges: func.isRequired, onSetRange: func.isRequired, - yRange: shape({ - min: string, - max: string, + yRanges: shape({ + y: array, + y2: array, }).isRequired, } diff --git a/ui/src/dashboards/containers/DashboardPage.js b/ui/src/dashboards/containers/DashboardPage.js index d610d371d9..a509105d63 100644 --- a/ui/src/dashboards/containers/DashboardPage.js +++ b/ui/src/dashboards/containers/DashboardPage.js @@ -256,7 +256,7 @@ class DashboardPage extends Component { inPresentationMode, handleChooseAutoRefresh, handleClickPresentationButton, - params: {sourceID}, + params: {sourceID, dashboardID}, } = this.props const lowerType = lower && lower.includes('Z') ? 'timeStamp' : 'constant' @@ -330,6 +330,7 @@ class DashboardPage extends Component { {selectedCell ? : null} @@ -380,8 +382,8 @@ class DashboardPage extends Component { autoRefresh={autoRefresh} synchronizer={this.synchronizer} onAddCell={this.handleAddCell} - inPresentationMode={inPresentationMode} onEditCell={this.handleEditDashboardCell} + inPresentationMode={inPresentationMode} onPositionChange={this.handleUpdatePosition} onDeleteCell={this.handleDeleteDashboardCell} onRenameCell={this.handleRenameDashboardCell} @@ -421,6 +423,7 @@ DashboardPage.propTypes = { addDashboardCellAsync: func.isRequired, editDashboardCell: func.isRequired, renameDashboardCell: func.isRequired, + editCellRanges: func.isRequired, }).isRequired, dashboards: arrayOf( shape({ diff --git a/ui/src/dashboards/reducers/ui.js b/ui/src/dashboards/reducers/ui.js index 810e825182..d2db908310 100644 --- a/ui/src/dashboards/reducers/ui.js +++ b/ui/src/dashboards/reducers/ui.js @@ -241,19 +241,19 @@ export default function ui(state = initialState, action) { } case 'EDIT_CELL_RANGES': { - const {dashboard, cell, ranges} = action.payload + const {dashboardID, cellID, yRanges} = action.payload const dashboards = state.dashboards.map( d => - (d.id === dashboard.id + (d.id === dashboardID ? { ...d, cells: d.cells.map( c => - (c.i === cell.i + (c.i === cellID ? { ...c, - ranges, + yRanges, } : c) ), From dba8359cd19f6fddfc0738e4d49cade984ff0a16 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Mon, 10 Jul 2017 13:19:38 -0700 Subject: [PATCH 13/93] Prettier --- ui/src/dashboards/containers/DashboardPage.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ui/src/dashboards/containers/DashboardPage.js b/ui/src/dashboards/containers/DashboardPage.js index a509105d63..802f983aa4 100644 --- a/ui/src/dashboards/containers/DashboardPage.js +++ b/ui/src/dashboards/containers/DashboardPage.js @@ -82,8 +82,7 @@ class DashboardPage extends Component { handleCloseTemplateManager(isEdited) { if ( - !isEdited || - (isEdited && confirm('Do you want to close without saving?')) // eslint-disable-line no-alert + !isEdited || (isEdited && confirm('Do you want to close without saving?')) // eslint-disable-line no-alert ) { this.setState({isTemplating: false}) } From edebfa2cbfb9f0f4474843aa0456e4bd9dce54de Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 11 Jul 2017 11:36:26 -0700 Subject: [PATCH 14/93] Save range with entire cell --- ui/spec/dashboards/reducers/uiSpec.js | 17 ---------- ui/src/dashboards/actions/index.js | 9 ----- .../components/CellEditorOverlay.js | 26 +++++--------- .../dashboards/components/DisplayOptions.js | 8 +---- ui/src/dashboards/components/Ranger.js | 8 ++--- ui/src/dashboards/containers/DashboardPage.js | 2 -- ui/src/dashboards/reducers/ui.js | 24 ------------- ui/src/data_explorer/components/VisView.js | 5 ++- .../data_explorer/components/Visualization.js | 3 ++ ui/src/shared/components/Dygraph.js | 16 ++++----- ui/src/shared/components/LineGraph.js | 34 ++++--------------- ui/src/shared/parsing/getRangeForDygraph.js | 11 +++--- 12 files changed, 38 insertions(+), 125 deletions(-) diff --git a/ui/spec/dashboards/reducers/uiSpec.js b/ui/spec/dashboards/reducers/uiSpec.js index 5d21fb829b..88183bb79c 100644 --- a/ui/spec/dashboards/reducers/uiSpec.js +++ b/ui/spec/dashboards/reducers/uiSpec.js @@ -11,7 +11,6 @@ import { renameDashboardCell, syncDashboardCell, templateVariableSelected, - editCellRanges, } from 'src/dashboards/actions' let state @@ -170,20 +169,4 @@ describe('DataExplorer.Reducers.UI', () => { expect(actual.dashboards[0].templates[0].values[1].selected).to.equal(false) expect(actual.dashboards[0].templates[0].values[2].selected).to.equal(true) }) - - it('an set the range', () => { - const dash = {..._.cloneDeep(d1), cells: [c1]} - - state = { - dashboards: [dash], - } - - const y = [1, 2] - const y2 = [null, null] - - const actual = reducer(state, editCellRanges(dash.id, c1.i, {y, y2})) - - expect(actual.dashboards[0].cells[0].yRanges.y).to.deep.equal(y) - expect(actual.dashboards[0].cells[0].yRanges.y2).to.deep.equal(y2) - }) }) diff --git a/ui/src/dashboards/actions/index.js b/ui/src/dashboards/actions/index.js index 2a4841759e..1ee23908f2 100644 --- a/ui/src/dashboards/actions/index.js +++ b/ui/src/dashboards/actions/index.js @@ -79,15 +79,6 @@ export const addDashboardCell = (dashboard, cell) => ({ }, }) -export const editCellRanges = (dashboardID, cellID, yRanges) => ({ - type: 'EDIT_CELL_RANGES', - payload: { - dashboardID, - cellID, - yRanges, - }, -}) - export const editDashboardCell = (dashboard, x, y, isEditing) => ({ type: 'EDIT_DASHBOARD_CELL', // x and y coords are used as a alternative to cell ids, which are not diff --git a/ui/src/dashboards/components/CellEditorOverlay.js b/ui/src/dashboards/components/CellEditorOverlay.js index 7ef54ddfc8..80b9468f2c 100644 --- a/ui/src/dashboards/components/CellEditorOverlay.js +++ b/ui/src/dashboards/components/CellEditorOverlay.js @@ -28,7 +28,6 @@ class CellEditorOverlay extends Component { this.handleDeleteQuery = ::this.handleDeleteQuery this.handleSaveCell = ::this.handleSaveCell - this.handleEditCellRanges = ::this.handleEditCellRanges this.handleSelectGraphType = ::this.handleSelectGraphType this.handleSelectDisplayOptions = ::this.handleSelectDisplayOptions @@ -81,14 +80,6 @@ class CellEditorOverlay extends Component { } } - handleEditCellRanges(e) { - e.preventDefault() - const {dashboardID, cell: {i}} = this.props - const {yRanges} = this.state - - this.props.onEditCellRanges(+dashboardID, i, yRanges) - } - handleSetRange(e) { const {min, max} = e.target.form this.setState({ @@ -113,13 +104,16 @@ class CellEditorOverlay extends Component { } handleSaveCell() { - const {queriesWorkingDraft, cellWorkingType, cellWorkingName} = this.state + const { + queriesWorkingDraft, + cellWorkingType: name, + cellWorkingName: type, + yRanges, + } = this.state + const {cell} = this.props - const newCell = _.cloneDeep(cell) - newCell.name = cellWorkingName - newCell.type = cellWorkingType - newCell.queries = queriesWorkingDraft.map(q => { + const queries = queriesWorkingDraft.map(q => { const timeRange = q.range || {upper: null, lower: ':dashboardTime:'} const query = q.rawText || buildInfluxQLQuery(timeRange, q) const label = q.rawText ? '' : `${q.measurement}.${q.fields[0].field}` @@ -131,7 +125,7 @@ class CellEditorOverlay extends Component { } }) - this.props.onSave(newCell) + this.props.onSave({...cell, name, type, queries, yRanges}) } handleSelectGraphType(graphType) { @@ -224,7 +218,6 @@ class CellEditorOverlay extends Component { /> {isDisplayOptionsTabOpen ? - +
    ) const {array, func, shape, string} = PropTypes DisplayOptions.propTypes = { - onEditCellRanges: func.isRequired, selectedGraphType: string.isRequired, onSelectGraphType: func.isRequired, onSetRange: func.isRequired, diff --git a/ui/src/dashboards/components/Ranger.js b/ui/src/dashboards/components/Ranger.js index 349ab05acb..238ab74ee0 100644 --- a/ui/src/dashboards/components/Ranger.js +++ b/ui/src/dashboards/components/Ranger.js @@ -1,8 +1,8 @@ import React, {PropTypes} from 'react' -const Ranger = ({onSetRange, yRanges, onEditCellRanges}) => ( +const Ranger = ({onSetRange, yRanges}) => (
    -
    + ( onChange={onSetRange} placeholder="auto" /> -
    ) @@ -33,7 +30,6 @@ const Ranger = ({onSetRange, yRanges, onEditCellRanges}) => ( const {array, func, shape} = PropTypes Ranger.propTypes = { - onEditCellRanges: func.isRequired, onSetRange: func.isRequired, yRanges: shape({ y: array, diff --git a/ui/src/dashboards/containers/DashboardPage.js b/ui/src/dashboards/containers/DashboardPage.js index 802f983aa4..2866747950 100644 --- a/ui/src/dashboards/containers/DashboardPage.js +++ b/ui/src/dashboards/containers/DashboardPage.js @@ -337,7 +337,6 @@ class DashboardPage extends Component { queryStatus={cellQueryStatus} onSave={this.handleSaveEditedCell} onCancel={this.handleDismissOverlay} - onEditCellRanges={dashboardActions.editCellRanges} editQueryStatus={dashboardActions.editCellQueryStatus} /> : null} @@ -422,7 +421,6 @@ DashboardPage.propTypes = { addDashboardCellAsync: func.isRequired, editDashboardCell: func.isRequired, renameDashboardCell: func.isRequired, - editCellRanges: func.isRequired, }).isRequired, dashboards: arrayOf( shape({ diff --git a/ui/src/dashboards/reducers/ui.js b/ui/src/dashboards/reducers/ui.js index d2db908310..92ae13c688 100644 --- a/ui/src/dashboards/reducers/ui.js +++ b/ui/src/dashboards/reducers/ui.js @@ -239,30 +239,6 @@ export default function ui(state = initialState, action) { return {...state, dashboards} } - - case 'EDIT_CELL_RANGES': { - const {dashboardID, cellID, yRanges} = action.payload - - const dashboards = state.dashboards.map( - d => - (d.id === dashboardID - ? { - ...d, - cells: d.cells.map( - c => - (c.i === cellID - ? { - ...c, - yRanges, - } - : c) - ), - } - : d) - ) - - return {...state, dashboards} - } } return state diff --git a/ui/src/data_explorer/components/VisView.js b/ui/src/data_explorer/components/VisView.js index 057d605d34..a32063cd5f 100644 --- a/ui/src/data_explorer/components/VisView.js +++ b/ui/src/data_explorer/components/VisView.js @@ -10,6 +10,7 @@ const RefreshingSingleStat = AutoRefresh(SingleStat) const VisView = ({ view, queries, + yRanges, cellType, templates, autoRefresh, @@ -58,8 +59,9 @@ const VisView = ({ return ( (this.legendRef = el)} + legendRef={el => this.legendRef = el} onInputChange={this.handleLegendInputChange} onToggleFilter={this.handleToggleFilter} /> @@ -359,12 +359,12 @@ export default class Dygraph extends Component { } } -const {array, arrayOf, func, number, bool, shape, string} = PropTypes +const {array, arrayOf, func, bool, shape, string} = PropTypes Dygraph.propTypes = { ranges: shape({ - y: arrayOf(number), - y2: arrayOf(number), + y: arrayOf(string), + y2: arrayOf(string), }), timeSeries: array.isRequired, labels: array.isRequired, diff --git a/ui/src/shared/components/LineGraph.js b/ui/src/shared/components/LineGraph.js index 0ce517f8b9..3d6af1b4fe 100644 --- a/ui/src/shared/components/LineGraph.js +++ b/ui/src/shared/components/LineGraph.js @@ -15,9 +15,9 @@ export default React.createClass({ displayName: 'LineGraph', propTypes: { data: arrayOf(shape({}).isRequired).isRequired, - ranges: shape({ - y: arrayOf(number), - y2: arrayOf(number), + yRanges: shape({ + y: arrayOf(string), + y2: arrayOf(string), }), title: string, isFetchingInitially: bool, @@ -67,8 +67,7 @@ export default React.createClass({ componentWillUpdate(nextProps) { const {data, activeQueryIndex} = this.props if ( - data !== nextProps.data || - activeQueryIndex !== nextProps.activeQueryIndex + data !== nextProps.data || activeQueryIndex !== nextProps.activeQueryIndex ) { this._timeSeries = timeSeriesToDygraph( nextProps.data, @@ -81,7 +80,7 @@ export default React.createClass({ render() { const { data, - ranges, + yRanges, isFetchingInitially, isRefreshing, isGraphFilled, @@ -170,7 +169,7 @@ export default React.createClass({ labels={labels} options={showSingleStat ? singleStatOptions : options} dygraphSeries={dygraphSeries} - ranges={ranges || this.getRanges()} + ranges={yRanges} ruleValues={ruleValues} synchronizer={synchronizer} timeRange={timeRange} @@ -200,25 +199,4 @@ export default React.createClass({
    ) }, - - getRanges() { - const {queries} = this.props - if (!queries) { - return {} - } - - const ranges = {} - const q0 = queries[0] - const q1 = queries[1] - - if (q0 && q0.range) { - ranges.y = [q0.range.lower, q0.range.upper] - } - - if (q1 && q1.range) { - ranges.y2 = [q1.range.lower, q1.range.upper] - } - - return ranges - }, }) diff --git a/ui/src/shared/parsing/getRangeForDygraph.js b/ui/src/shared/parsing/getRangeForDygraph.js index f92566a63b..6a162708d0 100644 --- a/ui/src/shared/parsing/getRangeForDygraph.js +++ b/ui/src/shared/parsing/getRangeForDygraph.js @@ -2,13 +2,9 @@ const PADDING_FACTOR = 0.1 export default function getRange( timeSeries, - override, + userSelectedRange = [null, null], ruleValues = {value: null, rangeValue: null} ) { - if (override) { - return override - } - const {value, rangeValue, operator} = ruleValues const subtractPadding = val => +val - Math.abs(val * PADDING_FACTOR) @@ -57,5 +53,8 @@ export default function getRange( return [null, null] } - return range + const [userMin, userMax] = userSelectedRange + const [min, max] = range + + return [+userMin || min, +userMax || max] } From 549ad92d9c90f43ac66368967c3fabf2fa4359e0 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 11 Jul 2017 11:53:08 -0700 Subject: [PATCH 15/93] Mock persistence of ranges --- ui/src/dashboards/actions/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/src/dashboards/actions/index.js b/ui/src/dashboards/actions/index.js index 1ee23908f2..917a93da72 100644 --- a/ui/src/dashboards/actions/index.js +++ b/ui/src/dashboards/actions/index.js @@ -178,7 +178,8 @@ export const putDashboardByID = dashboardID => async (dispatch, getState) => { export const updateDashboardCell = (dashboard, cell) => async dispatch => { try { const {data} = await updateDashboardCellAJAX(cell) - dispatch(syncDashboardCell(dashboard, data)) + // TODO: remove yRanges when server persists the ranges + dispatch(syncDashboardCell(dashboard, {...data, yRanges: cell.yRanges})) } catch (error) { console.error(error) dispatch(errorThrown(error)) From a15366febb29da0654be98d0b04ea874eb77f67c Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 11 Jul 2017 11:53:38 -0700 Subject: [PATCH 16/93] Wire up ranges to the rest of dashboard --- ui/src/shared/components/AutoRefresh.js | 4 ++++ ui/src/shared/components/LayoutRenderer.js | 3 ++- ui/src/shared/components/RefreshingGraph.js | 6 ++++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/ui/src/shared/components/AutoRefresh.js b/ui/src/shared/components/AutoRefresh.js index e0c3265e12..d1cf4ef7e3 100644 --- a/ui/src/shared/components/AutoRefresh.js +++ b/ui/src/shared/components/AutoRefresh.js @@ -43,6 +43,10 @@ const AutoRefresh = ComposedComponent => { text: string, }).isRequired ).isRequired, + yRanges: shape({ + y: arrayOf(string), + y2: arrayOf(string), + }), editQueryStatus: func, }, diff --git a/ui/src/shared/components/LayoutRenderer.js b/ui/src/shared/components/LayoutRenderer.js index bf5c154c87..bba4fbc829 100644 --- a/ui/src/shared/components/LayoutRenderer.js +++ b/ui/src/shared/components/LayoutRenderer.js @@ -151,7 +151,7 @@ class LayoutRenderer extends Component { } = this.props return cells.map(cell => { - const {type, h} = cell + const {type, h, yRanges} = cell return (
    @@ -175,6 +175,7 @@ class LayoutRenderer extends Component { type={type} queries={this.standardizeQueries(cell, source)} cellHeight={h} + yRanges={yRanges} />}
    diff --git a/ui/src/shared/components/RefreshingGraph.js b/ui/src/shared/components/RefreshingGraph.js index 65d5382dab..746f77c442 100644 --- a/ui/src/shared/components/RefreshingGraph.js +++ b/ui/src/shared/components/RefreshingGraph.js @@ -15,6 +15,7 @@ const RefreshingGraph = ({ type, queries, cellHeight, + yRanges, }) => { if (type === 'single-stat') { return ( @@ -42,6 +43,7 @@ const RefreshingGraph = ({ isBarGraph={type === 'bar'} displayOptions={displayOptions} synchronizer={synchronizer} + yRanges={yRanges} /> ) } @@ -58,6 +60,10 @@ RefreshingGraph.propTypes = { type: string.isRequired, queries: arrayOf(shape()).isRequired, cellHeight: number.isRequired, + yRanges: shape({ + y: arrayOf(string), + y2: arrayOf(string), + }), } export default RefreshingGraph From 6490a6efbdd778f9874921c84670138797d7e488 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 11 Jul 2017 11:53:51 -0700 Subject: [PATCH 17/93] Prettier --- ui/src/shared/components/AutoRefresh.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ui/src/shared/components/AutoRefresh.js b/ui/src/shared/components/AutoRefresh.js index d1cf4ef7e3..3679a9fe82 100644 --- a/ui/src/shared/components/AutoRefresh.js +++ b/ui/src/shared/components/AutoRefresh.js @@ -173,8 +173,7 @@ const AutoRefresh = ComposedComponent => { } if ( - this._noResultsForQuery(timeSeries) || - !this.state.lastQuerySuccessful + this._noResultsForQuery(timeSeries) || !this.state.lastQuerySuccessful ) { return this.renderNoResults() } From 131ddd2c91f7c98dfdef08ddbabf8b0117fdde76 Mon Sep 17 00:00:00 2001 From: Alex P Date: Thu, 13 Jul 2017 14:32:11 -0700 Subject: [PATCH 18/93] Style CEO Display Options Using inline SVGs for visualization type selection, moved the whole graphTypes object into a different location and file structure --- .../dashboards/components/DisplayOptions.js | 13 +- .../components/GraphTypeSelector.js | 38 ++- .../dashboards/components/OverlayControls.js | 43 ++- ui/src/dashboards/components/Ranger.js | 50 ++-- ui/src/dashboards/graphics/graph.js | 266 ++++++++++++++++++ ui/src/shared/data/graphTypes.hson | 12 +- ui/src/style/chronograf.scss | 2 +- .../style/components/ceo-display-options.scss | 140 +++++++++ 8 files changed, 481 insertions(+), 83 deletions(-) create mode 100644 ui/src/dashboards/graphics/graph.js create mode 100644 ui/src/style/components/ceo-display-options.scss diff --git a/ui/src/dashboards/components/DisplayOptions.js b/ui/src/dashboards/components/DisplayOptions.js index c9b6758f0f..e9f098cd09 100644 --- a/ui/src/dashboards/components/DisplayOptions.js +++ b/ui/src/dashboards/components/DisplayOptions.js @@ -3,28 +3,19 @@ import React, {PropTypes} from 'react' import GraphTypeSelector from 'src/dashboards/components/GraphTypeSelector' import Ranger from 'src/dashboards/components/Ranger' -const style = { - height: '100%', - margin: '0 60px', - display: 'flex', - backgroundColor: '#202028', - justifyContent: 'space-around', -} - const DisplayOptions = ({ selectedGraphType, onSelectGraphType, onSetRange, yRanges, -}) => ( -
    +}) => +
    -) const {array, func, shape, string} = PropTypes diff --git a/ui/src/dashboards/components/GraphTypeSelector.js b/ui/src/dashboards/components/GraphTypeSelector.js index e57640e093..15561447a8 100644 --- a/ui/src/dashboards/components/GraphTypeSelector.js +++ b/ui/src/dashboards/components/GraphTypeSelector.js @@ -1,28 +1,26 @@ import React, {PropTypes} from 'react' import classnames from 'classnames' +import {graphTypes} from 'src/dashboards/graphics/graph' -import graphTypes from 'hson!shared/data/graphTypes.hson' - -const GraphTypeSelector = ({selectedGraphType, onSelectGraphType}) => ( -
    -
    -

    Visualization Type

    -
      - {graphTypes.map(graphType => ( -
    • onSelectGraphType(graphType.type)} - > - {graphType.menuOption} -
    • - ))} -
    +const GraphTypeSelector = ({selectedGraphType, onSelectGraphType}) => +
    +
    Visualization Type
    +
    + {graphTypes.map(graphType => +
    +
    onSelectGraphType(graphType.type)}> + {graphType.graphic} +

    {graphType.menuOption}

    +
    +
    + )}
    -) const {func, string} = PropTypes diff --git a/ui/src/dashboards/components/OverlayControls.js b/ui/src/dashboards/components/OverlayControls.js index 8254f28e9e..7eb510c1fa 100644 --- a/ui/src/dashboards/components/OverlayControls.js +++ b/ui/src/dashboards/components/OverlayControls.js @@ -9,30 +9,30 @@ const OverlayControls = ({ isDisplayOptionsTabOpen, onSelectDisplayOptions, isSavable, -}) => ( +}) =>

    Cell Editor

    +
      +
    • + Queries +
    • +
    • + Display Options +
    • +
    -
      -
    • - Queries -
    • -
    • - Display Options -
    • -
    -) const {func, bool} = PropTypes diff --git a/ui/src/dashboards/components/Ranger.js b/ui/src/dashboards/components/Ranger.js index 238ab74ee0..c973756881 100644 --- a/ui/src/dashboards/components/Ranger.js +++ b/ui/src/dashboards/components/Ranger.js @@ -1,31 +1,35 @@ import React, {PropTypes} from 'react' -const Ranger = ({onSetRange, yRanges}) => ( -
    +const Ranger = ({onSetRange, yRanges}) => +
    +
    Y Axis Controls
    - - - - +
    + + +
    +
    + + +
    -) const {array, func, shape} = PropTypes diff --git a/ui/src/dashboards/graphics/graph.js b/ui/src/dashboards/graphics/graph.js new file mode 100644 index 0000000000..f0975c1573 --- /dev/null +++ b/ui/src/dashboards/graphics/graph.js @@ -0,0 +1,266 @@ +import React from 'react' + +export const graphTypes = [ + { + type: 'line', + menuOption: 'Line', + graphic: ( +
    + + + + + + + + +
    + ), + }, + { + type: 'line-stacked', + menuOption: 'Stacked', + graphic: ( +
    + + + + + + + + +
    + ), + }, + { + type: 'line-stepplot', + menuOption: 'Step-Plot', + graphic: ( +
    + + + + + + +
    + ), + }, + { + type: 'single-stat', + menuOption: 'SingleStat', + graphic: ( +
    + + + + + + +
    + ), + }, + { + type: 'line-plus-single-stat', + menuOption: 'Line + Stat', + graphic: ( +
    + + + + + + + + + + +
    + ), + }, + { + type: 'bar', + menuOption: 'Bar', + graphic: ( +
    + + + + + + + + + + + + + + +
    + ), + }, +] diff --git a/ui/src/shared/data/graphTypes.hson b/ui/src/shared/data/graphTypes.hson index 30a3565ab5..31724eb722 100644 --- a/ui/src/shared/data/graphTypes.hson +++ b/ui/src/shared/data/graphTypes.hson @@ -1,8 +1,8 @@ [ - {type: "line", menuOption: "Line"}, - {type: "line-stacked", menuOption: "Stacked"}, - {type: "line-stepplot", menuOption: "Step-Plot"}, - {type: "single-stat", menuOption: "SingleStat"}, - {type: "line-plus-single-stat", menuOption: "Line + Stat"}, - {type: "bar", menuOption: "Bar"}, + {type: "line", menuOption: "Line", graphic: "Wogglez"}, + {type: "line-stacked", menuOption: "Stacked", graphic: "Rogglez"}, + {type: "line-stepplot", menuOption: "Step-Plot", graphic: "Fogglez"}, + {type: "single-stat", menuOption: "SingleStat", graphic: "Bogglez"}, + {type: "line-plus-single-stat", menuOption: "Line + Stat", graphic: "Togglez"}, + {type: "bar", menuOption: "Bar", graphic: "Zogglez"}, ] diff --git a/ui/src/style/chronograf.scss b/ui/src/style/chronograf.scss index 1725aadc96..55094c8cf9 100644 --- a/ui/src/style/chronograf.scss +++ b/ui/src/style/chronograf.scss @@ -25,6 +25,7 @@ @import 'layout/flash-messages'; // Components +@import 'components/ceo-display-options'; @import 'components/confirm-buttons'; @import 'components/custom-time-range'; @import 'components/dygraphs'; @@ -47,7 +48,6 @@ @import 'components/tables'; // Pages - @import 'pages/config-endpoints'; @import 'pages/signup'; @import 'pages/auth-page'; diff --git a/ui/src/style/components/ceo-display-options.scss b/ui/src/style/components/ceo-display-options.scss new file mode 100644 index 0000000000..62a9a6eac8 --- /dev/null +++ b/ui/src/style/components/ceo-display-options.scss @@ -0,0 +1,140 @@ +/* + Cell Editor Overlay - Display Options + ------------------------------------------------------ +*/ +.display-options { + height: 100%; + margin: 0 60px; + display: flex; + background-color: $g2-kevlar; + padding: 0 8px 8px 8px; + border-radius: 0 0 4px 4px; + flex-wrap: nowrap; + align-items: stretch; +} +.display-options--cell { + border-radius: 3px; + background-color: $g3-castle; + padding: 30px; + flex: 1 0 0; + margin-right: 8px; + + &:last-child { margin-right: 0; } +} +.display-options--cellx2 { + flex: 2 0 0; +} +.display-options--header { + margin: 0 0 12px 0; + font-weight: 400; + color: $g11-sidewalk; + @include no-user-select(); +} +.display-options--row { + display: flex; + align-items: center; + flex-wrap: nowrap; + margin-bottom: 8px; + + label { margin: 0; } + input { flex: 1 0 0; } +} + + +.viz-type-selector { + display: flex; + flex-wrap: wrap; + height: calc(100% - 22px); + margin: -4px; +} +.viz-type-selector--option { + flex: 1 0 33.3333%; + height: 50%; + padding: 4px; + + > div > p { + margin: 0; + font-size: 14px; + font-weight: 900; + position: absolute; + bottom: 6.25%; + left: 50%; + transform: translate(-50%, 50%); + display: inline-block; + } + + // Actual "card" + > div { + background-color: $g3-castle; + border: 2px solid $g4-onyx; + color: $g11-sidewalk; + border-radius: 3px; + width: 100%; + height: 100%; + display: block; + position: relative; + transition: + color 0.25s ease, + border-color 0.25s ease, + background-color 0.25s ease; + + &:hover { + cursor: pointer; + background-color: $g4-onyx; + border-color: $g5-pepper; + color: $g15-platinum; + } + } +} +// Active state "card" +.viz-type-selector--option.active > div, +.viz-type-selector--option.active > div:hover { + background-color: $g5-pepper; + border-color: $g7-graphite; + color: $g18-cloud; +} + +.viz-type-selector--graphic { + width: calc(100% - 48px); + height: 50%; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%,-50%); + + > svg { + transform: translate3d(0,0,0); + width: 100%; + height: 100%; + } +} +.viz-type-selector--graphic-line { + stroke-width: 3px; + fill: none; + stroke-linecap: round; + stroke-miterlimit: 10; + transition: all 0.5s ease; + + &.graphic-line-a {stroke: $g11-sidewalk;} + &.graphic-line-b {stroke: $g9-mountain;} + &.graphic-line-c {stroke: $g7-graphite;} +} +.viz-type-selector--graphic-fill { + opacity: 0.035; + transition: opacity 0.5s ease; + + &.graphic-fill-a {fill: $g11-sidewalk;} + &.graphic-fill-b {fill: $g9-mountain;} + &.graphic-fill-c {fill: $g7-graphite;} +} +.viz-type-selector--option.active .viz-type-selector--graphic { + .viz-type-selector--graphic-line.graphic-line-a {stroke: $c-pool;} + .viz-type-selector--graphic-line.graphic-line-b {stroke: $c-dreamsicle;} + .viz-type-selector--graphic-line.graphic-line-c {stroke: $c-rainforest;} + .viz-type-selector--graphic-fill.graphic-fill-a {fill: $c-pool;} + .viz-type-selector--graphic-fill.graphic-fill-b {fill: $c-dreamsicle;} + .viz-type-selector--graphic-fill.graphic-fill-c {fill: $c-rainforest;} + .viz-type-selector--graphic-fill.graphic-fill-a, + .viz-type-selector--graphic-fill.graphic-fill-b, + .viz-type-selector--graphic-fill.graphic-fill-c {opacity: 0.18;} +} From 41d175e980e6fa29cfcbf48c45a19a0b078b5bae Mon Sep 17 00:00:00 2001 From: Alex P Date: Thu, 13 Jul 2017 14:47:12 -0700 Subject: [PATCH 19/93] Uncross the wires --- ui/src/dashboards/components/CellEditorOverlay.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/src/dashboards/components/CellEditorOverlay.js b/ui/src/dashboards/components/CellEditorOverlay.js index 80b9468f2c..caaa713551 100644 --- a/ui/src/dashboards/components/CellEditorOverlay.js +++ b/ui/src/dashboards/components/CellEditorOverlay.js @@ -106,8 +106,8 @@ class CellEditorOverlay extends Component { handleSaveCell() { const { queriesWorkingDraft, - cellWorkingType: name, - cellWorkingName: type, + cellWorkingType: type, + cellWorkingName: name, yRanges, } = this.state From 15e41284e3d9d637b8cac9b12ee1e79540f64760 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Mon, 24 Jul 2017 11:31:32 -0700 Subject: [PATCH 20/93] Handle user submitted zero --- ui/spec/shared/parsing/getRangeForDygraphSpec.js | 2 +- ui/src/shared/parsing/getRangeForDygraph.js | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/ui/spec/shared/parsing/getRangeForDygraphSpec.js b/ui/spec/shared/parsing/getRangeForDygraphSpec.js index d467a7c79f..2cec89bcfe 100644 --- a/ui/spec/shared/parsing/getRangeForDygraphSpec.js +++ b/ui/spec/shared/parsing/getRangeForDygraphSpec.js @@ -6,7 +6,7 @@ const mid = 10 const min = 5 const kapacitor = {value: null, rangeValue: null, operator: null} -describe('getRangeForDygraphSpec', () => { +describe.only('getRangeForDygraphSpec', () => { it('gets the range for one timeSeries', () => { const timeSeries = [[date, min], [date, mid], [date, max]] const actual = getRange(timeSeries) diff --git a/ui/src/shared/parsing/getRangeForDygraph.js b/ui/src/shared/parsing/getRangeForDygraph.js index 6a162708d0..2f2561e1b2 100644 --- a/ui/src/shared/parsing/getRangeForDygraph.js +++ b/ui/src/shared/parsing/getRangeForDygraph.js @@ -1,10 +1,18 @@ const PADDING_FACTOR = 0.1 -export default function getRange( +const considerZero = (userNumber, number) => { + if (typeof userNumber === 'number') { + return userNumber + } + + return number +} + +const getRange = ( timeSeries, userSelectedRange = [null, null], ruleValues = {value: null, rangeValue: null} -) { +) => { const {value, rangeValue, operator} = ruleValues const subtractPadding = val => +val - Math.abs(val * PADDING_FACTOR) @@ -56,5 +64,7 @@ export default function getRange( const [userMin, userMax] = userSelectedRange const [min, max] = range - return [+userMin || min, +userMax || max] + return [considerZero(userMin, min), considerZero(userMax, max)] } + +export default getRange From 99501b3897461eb74113ae2463e1e522714328d0 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Mon, 24 Jul 2017 15:01:23 -0700 Subject: [PATCH 21/93] WIP encorporate new shape for yRanges => axes --- .../components/CellEditorOverlay.js | 29 ++++++++----- .../dashboards/components/DisplayOptions.js | 11 ++--- ui/src/dashboards/components/Ranger.js | 26 +++++++----- ui/src/data_explorer/components/VisView.js | 41 ++++--------------- .../data_explorer/components/Visualization.js | 12 ++++-- ui/src/shared/components/AutoRefresh.js | 12 ++++-- ui/src/shared/components/Dygraph.js | 32 ++++++++++----- ui/src/shared/components/LineGraph.js | 21 ++++++---- ui/src/shared/components/RefreshingGraph.js | 11 ++--- ui/src/shared/parsing/getRangeForDygraph.js | 8 +++- 10 files changed, 107 insertions(+), 96 deletions(-) diff --git a/ui/src/dashboards/components/CellEditorOverlay.js b/ui/src/dashboards/components/CellEditorOverlay.js index caaa713551..ae97b8d837 100644 --- a/ui/src/dashboards/components/CellEditorOverlay.js +++ b/ui/src/dashboards/components/CellEditorOverlay.js @@ -35,7 +35,7 @@ class CellEditorOverlay extends Component { this.handleEditRawText = ::this.handleEditRawText this.handleSetRange = ::this.handleSetRange - const {cell: {name, type, queries, yRanges}} = props + const {cell: {name, type, queries, axes}} = props const queriesWorkingDraft = _.cloneDeep( queries.map(({queryConfig}) => ({...queryConfig, id: uuid.v4()})) @@ -47,9 +47,7 @@ class CellEditorOverlay extends Component { queriesWorkingDraft, activeQueryIndex: 0, isDisplayOptionsTabOpen: false, - yRanges: { - y: yRanges && yRanges.y ? yRanges.y : ['', ''], - }, + axes, } } @@ -82,9 +80,12 @@ class CellEditorOverlay extends Component { handleSetRange(e) { const {min, max} = e.target.form + this.setState({ - yRanges: { - y: [min.value, max.value], + axes: { + y: { + bounds: [min.value, max.value], + }, }, }) e.preventDefault() @@ -108,7 +109,7 @@ class CellEditorOverlay extends Component { queriesWorkingDraft, cellWorkingType: type, cellWorkingName: name, - yRanges, + axes, } = this.state const {cell} = this.props @@ -125,7 +126,13 @@ class CellEditorOverlay extends Component { } }) - this.props.onSave({...cell, name, type, queries, yRanges}) + this.props.onSave({ + ...cell, + name, + type, + queries, + axes, + }) } handleSelectGraphType(graphType) { @@ -174,7 +181,7 @@ class CellEditorOverlay extends Component { cellWorkingType, isDisplayOptionsTabOpen, queriesWorkingDraft, - yRanges, + axes, } = this.state const queryActions = { @@ -205,7 +212,7 @@ class CellEditorOverlay extends Component { cellType={cellWorkingType} cellName={cellWorkingName} editQueryStatus={editQueryStatus} - yRanges={yRanges} + axes={axes} views={[]} />
    @@ -221,7 +228,7 @@ class CellEditorOverlay extends Component { selectedGraphType={cellWorkingType} onSelectGraphType={this.handleSelectGraphType} onSetRange={this.handleSetRange} - yRanges={yRanges} + axes={axes} /> :
    - +
    -const {array, func, shape, string} = PropTypes +const {func, shape, string} = PropTypes DisplayOptions.propTypes = { selectedGraphType: string.isRequired, onSelectGraphType: func.isRequired, onSetRange: func.isRequired, - yRanges: shape({ - y: array, - y2: array, - }).isRequired, + axes: shape({}).isRequired, } export default DisplayOptions diff --git a/ui/src/dashboards/components/Ranger.js b/ui/src/dashboards/components/Ranger.js index c973756881..c550fa393f 100644 --- a/ui/src/dashboards/components/Ranger.js +++ b/ui/src/dashboards/components/Ranger.js @@ -1,29 +1,34 @@ import React, {PropTypes} from 'react' +import _ from 'lodash' -const Ranger = ({onSetRange, yRanges}) => +const Ranger = ({onSetRange, axes}) =>
    Y Axis Controls
    - +
    - + @@ -35,9 +40,10 @@ const {array, func, shape} = PropTypes Ranger.propTypes = { onSetRange: func.isRequired, - yRanges: shape({ - y: array, - y2: array, + axes: shape({ + y: shape({ + bounds: array, + }), }).isRequired, } diff --git a/ui/src/data_explorer/components/VisView.js b/ui/src/data_explorer/components/VisView.js index a32063cd5f..036d554c1a 100644 --- a/ui/src/data_explorer/components/VisView.js +++ b/ui/src/data_explorer/components/VisView.js @@ -1,23 +1,18 @@ import React, {PropTypes} from 'react' import Table from './Table' -import AutoRefresh from 'shared/components/AutoRefresh' -import LineGraph from 'shared/components/LineGraph' -import SingleStat from 'shared/components/SingleStat' -const RefreshingLineGraph = AutoRefresh(LineGraph) -const RefreshingSingleStat = AutoRefresh(SingleStat) +import RefreshingGraph from 'shared/components/RefreshingGraph' const VisView = ({ + axes, view, queries, - yRanges, cellType, templates, autoRefresh, heightPixels, editQueryStatus, activeQueryIndex, - isInDataExplorer, }) => { const activeQuery = queries[activeQueryIndex] const defaultQuery = queries[0] @@ -41,42 +36,23 @@ const VisView = ({ ) } - if (cellType === 'single-stat') { - return ( - - ) - } - - const displayOptions = { - stepPlot: cellType === 'line-stepplot', - stackedGraph: cellType === 'line-stacked', - } - return ( - ) } -const {arrayOf, bool, func, number, shape, string} = PropTypes +const {arrayOf, func, number, shape, string} = PropTypes VisView.propTypes = { view: string.isRequired, - yRanges: shape(), + axes: shape().isRequired, queries: arrayOf(shape()).isRequired, cellType: string, templates: arrayOf(shape()), @@ -84,7 +60,6 @@ VisView.propTypes = { heightPixels: number, editQueryStatus: func.isRequired, activeQueryIndex: number, - isInDataExplorer: bool, } export default VisView diff --git a/ui/src/data_explorer/components/Visualization.js b/ui/src/data_explorer/components/Visualization.js index c03c5a23fc..4729009040 100644 --- a/ui/src/data_explorer/components/Visualization.js +++ b/ui/src/data_explorer/components/Visualization.js @@ -6,7 +6,7 @@ import VisView from 'src/data_explorer/components/VisView' import {GRAPH, TABLE} from 'shared/constants' import _ from 'lodash' -const {arrayOf, bool, func, number, shape, string} = PropTypes +const {array, arrayOf, bool, func, number, shape, string} = PropTypes const META_QUERY_REGEX = /^show/i const Visualization = React.createClass({ @@ -26,7 +26,11 @@ const Visualization = React.createClass({ heightPixels: number, editQueryStatus: func.isRequired, views: arrayOf(string).isRequired, - yRanges: shape(), + axes: shape({ + y: shape({ + bounds: array, + }), + }), }, contextTypes: { @@ -77,9 +81,9 @@ const Visualization = React.createClass({ render() { const { + axes, views, height, - yRanges, cellType, cellName, timeRange, @@ -118,7 +122,7 @@ const Visualization = React.createClass({ > { text: string, }).isRequired ).isRequired, - yRanges: shape({ - y: arrayOf(string), - y2: arrayOf(string), + axes: shape({ + bounds: shape({ + y: array, + y2: array, + }), }), editQueryStatus: func, }, @@ -173,7 +176,8 @@ const AutoRefresh = ComposedComponent => { } if ( - this._noResultsForQuery(timeSeries) || !this.state.lastQuerySuccessful + this._noResultsForQuery(timeSeries) || + !this.state.lastQuerySuccessful ) { return this.renderNoResults() } diff --git a/ui/src/shared/components/Dygraph.js b/ui/src/shared/components/Dygraph.js index 07381d7565..8b80312c4d 100644 --- a/ui/src/shared/components/Dygraph.js +++ b/ui/src/shared/components/Dygraph.js @@ -54,7 +54,7 @@ export default class Dygraph extends Component { const timeSeries = this.getTimeSeries() // dygraphSeries is a legend label and its corresponding y-axis e.g. {legendLabel1: 'y', legendLabel2: 'y2'}; const { - ranges, + axes, dygraphSeries, ruleValues, overrideLineColors, @@ -71,6 +71,9 @@ export default class Dygraph extends Component { finalLineColors = LINE_COLORS } + const yAxis = _.get(axes, ['y', 'bounds'], [null, null]) + const y2Axis = _.get(axes, ['y2', 'bounds'], undefined) + const defaultOptions = { plugins: [ new Dygraphs.Plugins.Crosshair({ @@ -92,10 +95,10 @@ export default class Dygraph extends Component { series: dygraphSeries, axes: { y: { - valueRange: getRange(timeSeries, ranges && ranges.y, ruleValues), + valueRange: getRange(timeSeries, yAxis, ruleValues), }, y2: { - valueRange: getRange(timeSeries, ranges && ranges.y2), + valueRange: getRange(timeSeries, y2Axis), }, }, highlightSeriesOpts: { @@ -235,7 +238,7 @@ export default class Dygraph extends Component { componentDidUpdate() { const { labels, - ranges, + axes, options, dygraphSeries, ruleValues, @@ -249,16 +252,19 @@ export default class Dygraph extends Component { ) } + const y = _.get(axes, ['y', 'bounds'], [null, null]) + const y2 = _.get(axes, ['y2', 'bounds'], undefined) const timeSeries = this.getTimeSeries() + const updateOptions = { labels, file: timeSeries, axes: { y: { - valueRange: getRange(timeSeries, ranges && ranges.y, ruleValues), + valueRange: getRange(timeSeries, y, ruleValues), }, y2: { - valueRange: getRange(timeSeries, ranges && ranges.y2), + valueRange: getRange(timeSeries, y2), }, }, stepPlot: options.stepPlot, @@ -343,7 +349,7 @@ export default class Dygraph extends Component { isAscending={isAscending} onSnip={this.handleSnipLabel} onSort={this.handleSortLegend} - legendRef={el => this.legendRef = el} + legendRef={el => (this.legendRef = el)} onInputChange={this.handleLegendInputChange} onToggleFilter={this.handleToggleFilter} /> @@ -359,12 +365,16 @@ export default class Dygraph extends Component { } } -const {array, arrayOf, func, bool, shape, string} = PropTypes +const {array, bool, func, shape, string} = PropTypes Dygraph.propTypes = { - ranges: shape({ - y: arrayOf(string), - y2: arrayOf(string), + axes: shape({ + y: shape({ + bounds: array, + }), + y2: shape({ + bounds: array, + }), }), timeSeries: array.isRequired, labels: array.isRequired, diff --git a/ui/src/shared/components/LineGraph.js b/ui/src/shared/components/LineGraph.js index 3d6af1b4fe..e92d4dfe8f 100644 --- a/ui/src/shared/components/LineGraph.js +++ b/ui/src/shared/components/LineGraph.js @@ -15,9 +15,13 @@ export default React.createClass({ displayName: 'LineGraph', propTypes: { data: arrayOf(shape({}).isRequired).isRequired, - yRanges: shape({ - y: arrayOf(string), - y2: arrayOf(string), + axes: shape({ + y: shape({ + bounds: array, + }), + y2: shape({ + bounds: array, + }), }), title: string, isFetchingInitially: bool, @@ -67,7 +71,8 @@ export default React.createClass({ componentWillUpdate(nextProps) { const {data, activeQueryIndex} = this.props if ( - data !== nextProps.data || activeQueryIndex !== nextProps.activeQueryIndex + data !== nextProps.data || + activeQueryIndex !== nextProps.activeQueryIndex ) { this._timeSeries = timeSeriesToDygraph( nextProps.data, @@ -80,7 +85,7 @@ export default React.createClass({ render() { const { data, - yRanges, + axes, isFetchingInitially, isRefreshing, isGraphFilled, @@ -159,6 +164,7 @@ export default React.createClass({ > {isRefreshing ? this.renderSpinner() : null} - {roundedValue} + + {roundedValue} +
    : null} diff --git a/ui/src/shared/components/RefreshingGraph.js b/ui/src/shared/components/RefreshingGraph.js index 746f77c442..56e43857ff 100644 --- a/ui/src/shared/components/RefreshingGraph.js +++ b/ui/src/shared/components/RefreshingGraph.js @@ -15,7 +15,7 @@ const RefreshingGraph = ({ type, queries, cellHeight, - yRanges, + axes, }) => { if (type === 'single-stat') { return ( @@ -43,7 +43,7 @@ const RefreshingGraph = ({ isBarGraph={type === 'bar'} displayOptions={displayOptions} synchronizer={synchronizer} - yRanges={yRanges} + axes={axes} /> ) } @@ -59,11 +59,8 @@ RefreshingGraph.propTypes = { synchronizer: func, type: string.isRequired, queries: arrayOf(shape()).isRequired, - cellHeight: number.isRequired, - yRanges: shape({ - y: arrayOf(string), - y2: arrayOf(string), - }), + cellHeight: number, + axes: shape(), } export default RefreshingGraph diff --git a/ui/src/shared/parsing/getRangeForDygraph.js b/ui/src/shared/parsing/getRangeForDygraph.js index 2f2561e1b2..1e505718b9 100644 --- a/ui/src/shared/parsing/getRangeForDygraph.js +++ b/ui/src/shared/parsing/getRangeForDygraph.js @@ -1,8 +1,12 @@ const PADDING_FACTOR = 0.1 const considerZero = (userNumber, number) => { - if (typeof userNumber === 'number') { - return userNumber + if (userNumber === '') { + return null + } + + if (userNumber) { + return +userNumber } return number From 6a9bc3639520ce0e84afca06b6a37dd5b828ac17 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Mon, 24 Jul 2017 15:08:01 -0700 Subject: [PATCH 22/93] WIP add range to dashboard cell --- .../components/CellEditorOverlay.js | 20 ++++- ui/src/dashboards/components/Ranger.js | 74 ++++++++++--------- ui/src/shared/components/LayoutRenderer.js | 4 +- 3 files changed, 61 insertions(+), 37 deletions(-) diff --git a/ui/src/dashboards/components/CellEditorOverlay.js b/ui/src/dashboards/components/CellEditorOverlay.js index ae97b8d837..001aa5fa01 100644 --- a/ui/src/dashboards/components/CellEditorOverlay.js +++ b/ui/src/dashboards/components/CellEditorOverlay.js @@ -34,6 +34,7 @@ class CellEditorOverlay extends Component { this.handleSetActiveQueryIndex = ::this.handleSetActiveQueryIndex this.handleEditRawText = ::this.handleEditRawText this.handleSetRange = ::this.handleSetRange + this.normalizeAxes = ::this.normalizeAxes const {cell: {name, type, queries, axes}} = props @@ -81,6 +82,7 @@ class CellEditorOverlay extends Component { handleSetRange(e) { const {min, max} = e.target.form + // TODO: handle "" for min and max value this.setState({ axes: { y: { @@ -109,10 +111,10 @@ class CellEditorOverlay extends Component { queriesWorkingDraft, cellWorkingType: type, cellWorkingName: name, - axes, } = this.state const {cell} = this.props + const axes = this.normalizeAxes() const queries = queriesWorkingDraft.map(q => { const timeRange = q.range || {upper: null, lower: ':dashboardTime:'} @@ -135,6 +137,22 @@ class CellEditorOverlay extends Component { }) } + normalizeAxes() { + const axes = this.state.axes + const bounds = _.get(axes, ['y', 'bounds'], false) + if (!bounds && !bounds.length) { + return {...axes, y: {bounds: []}} + } + + const [min, max] = bounds + if (min === '' || max === '') { + // TODO: throw requirement error + return + } + + return {...axes, y: {bounds: [+min, +max]}} + } + handleSelectGraphType(graphType) { this.setState({cellWorkingType: graphType}) } diff --git a/ui/src/dashboards/components/Ranger.js b/ui/src/dashboards/components/Ranger.js index c550fa393f..2db044e0cc 100644 --- a/ui/src/dashboards/components/Ranger.js +++ b/ui/src/dashboards/components/Ranger.js @@ -1,40 +1,46 @@ import React, {PropTypes} from 'react' import _ from 'lodash' -const Ranger = ({onSetRange, axes}) => -
    -
    Y Axis Controls
    - -
    - - -
    -
    - - -
    - -
    +const Ranger = ({onSetRange, axes}) => { + const min = _.get(axes, ['y', 'bounds', '0'], '') + const max = _.get(axes, ['y', 'bounds', '1'], '') + + return ( +
    +
    Y Axis Controls
    +
    +
    + + +
    +
    + + +
    +
    +
    + ) +} const {array, func, shape} = PropTypes diff --git a/ui/src/shared/components/LayoutRenderer.js b/ui/src/shared/components/LayoutRenderer.js index bba4fbc829..5408f54b5b 100644 --- a/ui/src/shared/components/LayoutRenderer.js +++ b/ui/src/shared/components/LayoutRenderer.js @@ -151,7 +151,7 @@ class LayoutRenderer extends Component { } = this.props return cells.map(cell => { - const {type, h, yRanges} = cell + const {type, h, axes} = cell return (
    @@ -175,7 +175,7 @@ class LayoutRenderer extends Component { type={type} queries={this.standardizeQueries(cell, source)} cellHeight={h} - yRanges={yRanges} + axes={axes} />}
    From c2715b3263a73c09f09a5d984f7f177a356dab3e Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 27 Jul 2017 13:29:46 -0700 Subject: [PATCH 23/93] Give preference to queryConfig range --- ui/src/data_explorer/components/Visualization.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/src/data_explorer/components/Visualization.js b/ui/src/data_explorer/components/Visualization.js index 452c4f5a44..2b6b4d1574 100644 --- a/ui/src/data_explorer/components/Visualization.js +++ b/ui/src/data_explorer/components/Visualization.js @@ -93,7 +93,8 @@ const Visualization = React.createClass({ const {view} = this.state const statements = queryConfigs.map(query => { - const text = query.rawText || buildInfluxQLQuery(timeRange, query) + const text = + query.rawText || buildInfluxQLQuery(query.range || timeRange, query) return {text, id: query.id} }) const queries = statements.filter(s => s.text !== null).map(s => { From d88247849b9fad06d492348fda5dafc7e439ba68 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 27 Jul 2017 13:41:44 -0700 Subject: [PATCH 24/93] Update CHANGEOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bce5079bc6..b03cbb4416 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ## v1.3.6.0 [unreleased] ### Bug Fixes +1. [#1798](https://github.com/influxdata/chronograf/pull/1798): Fix domain on CEO not updating when new time is entered into InfluxQL in the builder ### Features ### UI Improvements From 753726d0f2eb11a238db674e1ee3e795ac667dd3 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 27 Jul 2017 14:38:57 -0700 Subject: [PATCH 25/93] Dont sync graphs if there is only one graph --- ui/src/dashboards/containers/DashboardPage.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ui/src/dashboards/containers/DashboardPage.js b/ui/src/dashboards/containers/DashboardPage.js index 82941d6801..9172e6ef10 100644 --- a/ui/src/dashboards/containers/DashboardPage.js +++ b/ui/src/dashboards/containers/DashboardPage.js @@ -209,7 +209,11 @@ class DashboardPage extends Component { const dygraphs = [...this.state.dygraphs, dygraph] const {dashboards, params} = this.props const dashboard = dashboards.find(d => d.id === +params.dashboardID) - if (dashboard && dygraphs.length === dashboard.cells.length) { + if ( + dashboard && + dygraphs.length === dashboard.cells.length && + dashboard.cells.length > 1 + ) { Dygraph.synchronize(dygraphs, { selection: true, zoom: false, From cb91c967edd2ea4c62311a8c69a57da7e9c5d0c9 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 27 Jul 2017 14:47:04 -0700 Subject: [PATCH 26/93] Update CHANGELOG --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3516dfe021..609fd49335 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ## v1.3.6.0 [unreleased] ### Bug Fixes +1. [#1799](https://github.com/influxdata/chronograf/pull/1799): Fix console error spam from Dygraph.syncronize + ### Features ### UI Improvements 1. [#1796](https://github.com/influxdata/chronograf/pull/1796): Add spinner to indicate data is being written From ddb170dcd6f9b6f65efc0da0b293b01e7a214629 Mon Sep 17 00:00:00 2001 From: Jared Scheib Date: Thu, 27 Jul 2017 14:48:18 -0700 Subject: [PATCH 27/93] Update release date --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bce5079bc6..acf262997f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ### Features ### UI Improvements -## v1.3.5.0 [2017-07-25] +## v1.3.5.0 [2017-07-27] ### Bug Fixes 1. [#1708](https://github.com/influxdata/chronograf/pull/1708): Fix z-index issue in dashboard cell context menu 1. [#1752](https://github.com/influxdata/chronograf/pull/1752): Clarify BoltPath server flag help text by making example the default path From 99eff45056bdbfb4d7957b69e9b033625e098fcb Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 27 Jul 2017 15:23:59 -0700 Subject: [PATCH 28/93] Embiggen the write data form --- ui/src/data_explorer/components/WriteDataBody.js | 2 +- ui/src/style/components/write-data-form.scss | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/src/data_explorer/components/WriteDataBody.js b/ui/src/data_explorer/components/WriteDataBody.js index 70b987a77e..2cb59caab0 100644 --- a/ui/src/data_explorer/components/WriteDataBody.js +++ b/ui/src/data_explorer/components/WriteDataBody.js @@ -33,7 +33,7 @@ const WriteDataBody = ({ ref={fileInput} accept="text/*, application/gzip" /> - {uploadContent diff --git a/ui/src/style/components/write-data-form.scss b/ui/src/style/components/write-data-form.scss index cb8ede6052..f3b8db936c 100644 --- a/ui/src/style/components/write-data-form.scss +++ b/ui/src/style/components/write-data-form.scss @@ -7,7 +7,7 @@ $write-data--max-width: 960px; $write-data--gutter: 30px; $write-data--margin: 18px; -$write-data--input-height: 120px; +$write-data--input-height: 80vh; $write-data--transition: opacity 0.4s ease; .write-data-form { From 0878539edd0c1113e811d16773f7ffb7b1db3181 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 27 Jul 2017 15:26:20 -0700 Subject: [PATCH 29/93] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3516dfe021..c6c5cc784d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Features ### UI Improvements 1. [#1796](https://github.com/influxdata/chronograf/pull/1796): Add spinner to indicate data is being written +1. [#1800](https://github.com/influxdata/chronograf/pull/1796): Embiggen line protocol INSERT text area ## v1.3.5.0 [2017-07-25] ### Bug Fixes From 7abb6dc170db9f57132ebabb3429868bfffdbf14 Mon Sep 17 00:00:00 2001 From: Jared Scheib Date: Thu, 27 Jul 2017 16:35:18 -0700 Subject: [PATCH 30/93] Clarify changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9097df7318..e1f00a2481 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ### Features ### UI Improvements 1. [#1796](https://github.com/influxdata/chronograf/pull/1796): Add spinner to indicate data is being written -1. [#1800](https://github.com/influxdata/chronograf/pull/1796): Embiggen line protocol INSERT text area +1. [#1800](https://github.com/influxdata/chronograf/pull/1796): Embiggen text area for line protocol manual entry in Data Explorer's Write Data overlay ## v1.3.5.0 [2017-07-27] ### Bug Fixes From 76614643ec6e8305093e6a14b64863acf6d1f219 Mon Sep 17 00:00:00 2001 From: Jared Scheib Date: Thu, 27 Jul 2017 16:49:18 -0700 Subject: [PATCH 31/93] Clarify changelog, fix method name typo --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 609fd49335..635c79f9f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ ## v1.3.6.0 [unreleased] ### Bug Fixes -1. [#1799](https://github.com/influxdata/chronograf/pull/1799): Fix console error spam from Dygraph.syncronize +1. [#1799](https://github.com/influxdata/chronograf/pull/1799): Prevent console error spam from Dygraph.synchronize when a dashboard has only one graph ### Features ### UI Improvements From 06de962b6621a2ec18c76deacbf2ede0b1e49494 Mon Sep 17 00:00:00 2001 From: Alex P Date: Thu, 27 Jul 2017 16:57:09 -0700 Subject: [PATCH 32/93] Prevent overlay from extending beyond the viewport Factored in the heights of all the neighboring elements --- ui/src/style/components/write-data-form.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/style/components/write-data-form.scss b/ui/src/style/components/write-data-form.scss index f3b8db936c..5a76ce6d4f 100644 --- a/ui/src/style/components/write-data-form.scss +++ b/ui/src/style/components/write-data-form.scss @@ -7,7 +7,7 @@ $write-data--max-width: 960px; $write-data--gutter: 30px; $write-data--margin: 18px; -$write-data--input-height: 80vh; +$write-data--input-height: calc(90vh - 48px - 60px - 36px); // Heights of everything but input height $write-data--transition: opacity 0.4s ease; .write-data-form { From 1292757b097ff2b9213ac989b42599a0e9eb60a9 Mon Sep 17 00:00:00 2001 From: Hunter Trujillo Date: Fri, 28 Jul 2017 18:20:22 -0600 Subject: [PATCH 33/93] Add universal hashed colors algorithm. Fix bar graph column overlap. Remove crosshairs on bar graphs. --- ui/src/shared/components/Dygraph.js | 48 +++++++++++++++++++++++------ ui/src/shared/graphs/helpers.js | 32 ++++++++++++------- 2 files changed, 60 insertions(+), 20 deletions(-) diff --git a/ui/src/shared/components/Dygraph.js b/ui/src/shared/components/Dygraph.js index cfdb06968c..02e5ba101a 100644 --- a/ui/src/shared/components/Dygraph.js +++ b/ui/src/shared/components/Dygraph.js @@ -10,6 +10,12 @@ import getRange from 'shared/parsing/getRangeForDygraph' import {LINE_COLORS, multiColumnBarPlotter} from 'src/shared/graphs/helpers' import DygraphLegend from 'src/shared/components/DygraphLegend' +const hasherino = (str, len) => + str + .split('') + .map(char => char.charCodeAt(0)) + .reduce((hash, code) => (hash + code) % len, 0) + export default class Dygraph extends Component { constructor(props) { super(props) @@ -65,18 +71,42 @@ export default class Dygraph extends Component { const graphRef = this.graphRef const legendRef = this.legendRef - let finalLineColors = overrideLineColors + const finalLineColors = [...(overrideLineColors || LINE_COLORS)] - if (finalLineColors === null) { - finalLineColors = LINE_COLORS + const hashColorDygraphSeries = {} + const {length} = finalLineColors + + let used = [] + + for (const seriesName in dygraphSeries) { + const series = dygraphSeries[seriesName] + let hashIndex = hasherino(seriesName, length) + + // Check to see if color is already being used + while (used.includes(hashIndex)) { + hashIndex = (hashIndex + 1) % length + } + + used.push(hashIndex) + + // Empty used array if all colors are used + if (used.length === length) { + used = [] + } + + const color = finalLineColors[hashIndex] + + hashColorDygraphSeries[seriesName] = {...series, color} } const defaultOptions = { - plugins: [ - new Dygraphs.Plugins.Crosshair({ - direction: 'vertical', - }), - ], + plugins: isBarGraph + ? [] + : [ + new Dygraphs.Plugins.Crosshair({ + direction: 'vertical', + }), + ], labelsSeparateLines: false, labelsKMB: true, rightGap: 0, @@ -89,7 +119,7 @@ export default class Dygraph extends Component { animatedZooms: true, hideOverlayOnMouseOut: false, colors: finalLineColors, - series: dygraphSeries, + series: hashColorDygraphSeries, axes: { y: { valueRange: getRange(timeSeries, ranges.y, ruleValues), diff --git a/ui/src/shared/graphs/helpers.js b/ui/src/shared/graphs/helpers.js index a0dc843722..2470fafc5d 100644 --- a/ui/src/shared/graphs/helpers.js +++ b/ui/src/shared/graphs/helpers.js @@ -26,7 +26,7 @@ export const darkenColor = colorStr => { return `rgb(${color.r},${color.g},${color.b})` } -// Bar Graph code below is from http://dygraphs.com/tests/plotters.html +// Bar Graph code below is adapted from http://dygraphs.com/tests/plotters.html export const multiColumnBarPlotter = e => { // We need to handle all the series simultaneously. if (e.seriesIndex !== 0) { @@ -51,24 +51,32 @@ export const multiColumnBarPlotter = e => { } } - const barWidth = Math.floor(2.0 / 3 * minSep) + const barWidth = Math.max(Math.floor(2.0 / 3.0 * minSep), 1) const fillColors = [] const strokeColors = g.getColors() + + let selPointX + if (g.selPoints_ && g.selPoints_.length) { + selPointX = g.selPoints_[0].canvasx + } + for (let i = 0; i < strokeColors.length; i++) { fillColors.push(darkenColor(strokeColors[i])) } + ctx.lineWidth = 2 + for (let j = 0; j < sets.length; j++) { - ctx.fillStyle = fillColors[j] ctx.strokeStyle = strokeColors[j] for (let i = 0; i < sets[j].length; i++) { const p = sets[j][i] const centerX = p.canvasx + ctx.fillStyle = fillColors[j] const xLeft = sets.length === 1 - ? centerX - barWidth / 2 - : centerX - barWidth / 2 * (1 - j / (sets.length - 1)) + ? centerX - barWidth / 1 + : centerX - barWidth / 1 * (1 - j / sets.length) ctx.fillRect( xLeft, @@ -77,12 +85,14 @@ export const multiColumnBarPlotter = e => { yBottom - p.canvasy ) - ctx.strokeRect( - xLeft, - p.canvasy, - barWidth / sets.length, - yBottom - p.canvasy - ) + if (selPointX === centerX) { + ctx.strokeRect( + xLeft, + p.canvasy, + barWidth / sets.length, + yBottom - p.canvasy + ) + } } } } From 81c967f0b95a23a1fd74d3b3c00cad999291630a Mon Sep 17 00:00:00 2001 From: Hunter Trujillo Date: Mon, 31 Jul 2017 11:57:51 -0600 Subject: [PATCH 34/93] Refactor hashing function to be simpler and remove hash de-duplication. Remove highlight circle on bar graphs. --- ui/src/shared/components/Dygraph.js | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/ui/src/shared/components/Dygraph.js b/ui/src/shared/components/Dygraph.js index 02e5ba101a..ccd73ef39f 100644 --- a/ui/src/shared/components/Dygraph.js +++ b/ui/src/shared/components/Dygraph.js @@ -76,26 +76,10 @@ export default class Dygraph extends Component { const hashColorDygraphSeries = {} const {length} = finalLineColors - let used = [] - for (const seriesName in dygraphSeries) { const series = dygraphSeries[seriesName] - let hashIndex = hasherino(seriesName, length) - - // Check to see if color is already being used - while (used.includes(hashIndex)) { - hashIndex = (hashIndex + 1) % length - } - - used.push(hashIndex) - - // Empty used array if all colors are used - if (used.length === length) { - used = [] - } - + const hashIndex = hasherino(seriesName, length) const color = finalLineColors[hashIndex] - hashColorDygraphSeries[seriesName] = {...series, color} } @@ -115,7 +99,7 @@ export default class Dygraph extends Component { fillGraph: isGraphFilled, axisLineWidth: 2, gridLineWidth: 1, - highlightCircleSize: 3, + highlightCircleSize: isBarGraph ? 0 : 3, animatedZooms: true, hideOverlayOnMouseOut: false, colors: finalLineColors, @@ -130,7 +114,7 @@ export default class Dygraph extends Component { }, highlightSeriesOpts: { strokeWidth: 2, - highlightCircleSize: 5, + highlightCircleSize: isBarGraph ? 0 : 5, }, legendFormatter: legend => { if (!legend.x) { From 6a9e1d245751de974235a1e170138608a43d83c5 Mon Sep 17 00:00:00 2001 From: Hunter Trujillo Date: Mon, 31 Jul 2017 12:07:48 -0600 Subject: [PATCH 35/93] =?UTF-8?q?.=20.=20c=CC=8C=CD=90=CD=98=CC=82=CC=8B?= =?UTF-8?q?=CC=89=CD=9D=CC=A5=CD=88=CC=BA=CD=95=CC=AF=CC=A7H=CC=9A=CC=94?= =?UTF-8?q?=CD=9B=CC=8F=CD=A0=CC=84=CD=8D=CC=B3=CD=9F=CC=9F=CC=96=CC=A8?= =?UTF-8?q?=CC=B3=CC=B1=CD=96=CD=8E=CC=B8a=CD=9B=CC=8B=CD=8A=CC=BE=CD=9D?= =?UTF-8?q?=CC=81=CC=BB=CD=9F=CC=98=CC=AA=CC=9D=CC=A1=CC=A8=CC=A4=CC=BB?= =?UTF-8?q?=CC=B3N=CD=84=CC=95=CC=8E=CC=91=CC=8D=CD=8A=CC=95=CC=9A=CC=83?= =?UTF-8?q?=CD=80=CD=89=CD=9C=CD=87=CD=85=CC=A4=CD=94=CC=B7g=CD=83=CD=A0?= =?UTF-8?q?=CD=84=CD=8A=CD=9B=CD=88=CD=88=CC=AD=CC=99=CC=AC=CC=9C=CC=A2E?= =?UTF-8?q?=CC=BE=CC=81=CC=82=CD=98=CC=9B=CC=90=CD=8B=CC=86=CC=AB=CD=87?= =?UTF-8?q?=CD=9A=CC=98=CD=A2=CC=B9l=CD=90=CC=86=CD=98=CD=81=CD=83=CC=9B?= =?UTF-8?q?=CC=B2=CC=A7=CC=AE=CD=93=CD=85=CD=88=CC=BC=CD=89=CC=AE=CC=B4O?= =?UTF-8?q?=CC=8B=CC=BE=CC=92=CD=A0=CD=82=CD=97=CD=8B=CC=8E=CD=81=CC=9A?= =?UTF-8?q?=CC=A2=CC=A8=CC=A7=CD=95=CD=96=CC=B8g=CD=81=CC=8B=CD=8A=CD=8B?= =?UTF-8?q?=CC=8D=CC=A1=CD=A2=CD=99=CC=98=CC=96=CC=BA=CD=96=CD=9C=CD=89?= =?UTF-8?q?=CD=8E=CC=B5E=CC=89=CC=8D=CC=87=CC=84=CC=9A=CC=86=CC=B3=CC=A5?= =?UTF-8?q?=CC=B2=CD=87=CC=BB=20.=20.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d10faa318d..eca666284d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ ### UI Improvements 1. [#1796](https://github.com/influxdata/chronograf/pull/1796): Add spinner to indicate data is being written 1. [#1800](https://github.com/influxdata/chronograf/pull/1796): Embiggen text area for line protocol manual entry in Data Explorer's Write Data overlay +1. [#1805](https://github.com/influxdata/chronograf/pull/1805): Bar graphs no longer overlap with each other, and bonus, series names are hashed so that graph colors should stay the same for the same series across charts ## v1.3.5.0 [2017-07-27] ### Bug Fixes From 2ff3e27e1f4be9395e14dfb6d2a0e41dbbf9c347 Mon Sep 17 00:00:00 2001 From: Tim Raymond Date: Wed, 26 Jul 2017 15:45:17 -0400 Subject: [PATCH 36/93] Convert Axis Bounds to []string Due to various limitations with the previous implementation of Bounds as a [2]int64{}, we've decided to change this to a []string{}. This will allow clients to store arbitrary data specifying a bound and interpret it as they wish. --- bolt/internal/internal.go | 22 ++++-- bolt/internal/internal.pb.go | 124 +++++++++++++++++---------------- bolt/internal/internal.proto | 3 +- bolt/internal/internal_test.go | 75 +++++++++++++++++++- chronograf.go | 3 +- server/cells_test.go | 10 +-- server/dashboards_test.go | 8 +-- 7 files changed, 165 insertions(+), 80 deletions(-) diff --git a/bolt/internal/internal.go b/bolt/internal/internal.go index b9400a4899..cca08cdd10 100644 --- a/bolt/internal/internal.go +++ b/bolt/internal/internal.go @@ -2,6 +2,7 @@ package internal import ( "encoding/json" + "strconv" "github.com/gogo/protobuf/proto" "github.com/influxdata/chronograf" @@ -181,14 +182,14 @@ func MarshalDashboard(d chronograf.Dashboard) ([]byte, error) { axes := make(map[string]*Axis, len(c.Axes)) for a, r := range c.Axes { - // need to explicitly allocate a new array because r.Bounds is - // over-written and the resulting slices from previous iterations will - // point to later iteration's data. It is _not_ enough to simply re-slice - // r.Bounds + // we only marshal LegacyBounds for a data migration test. This should + // not be used by anything in production. axis := [2]int64{} - copy(axis[:], r.Bounds[:2]) + copy(axis[:], r.LegacyBounds[:2]) + axes[a] = &Axis{ - Bounds: axis[:], + Bounds: r.Bounds, + LegacyBounds: axis[:], } } @@ -268,7 +269,14 @@ func UnmarshalDashboard(data []byte, d *chronograf.Dashboard) error { axes := make(map[string]chronograf.Axis, len(c.Axes)) for a, r := range c.Axes { axis := chronograf.Axis{} - copy(axis.Bounds[:], r.Bounds[:2]) + // repair legacy bounds + for _, bound := range r.LegacyBounds { + axis.Bounds = append(axis.Bounds, strconv.FormatInt(bound, 10)) + } + + if len(r.Bounds) > 0 { + axis.Bounds = r.Bounds + } axes[a] = axis } diff --git a/bolt/internal/internal.pb.go b/bolt/internal/internal.pb.go index ccc0aedc9d..c81dd610fc 100644 --- a/bolt/internal/internal.pb.go +++ b/bolt/internal/internal.pb.go @@ -117,7 +117,8 @@ func (m *DashboardCell) GetAxes() map[string]*Axis { } type Axis struct { - Bounds []int64 `protobuf:"varint,1,rep,name=bounds" json:"bounds,omitempty"` + LegacyBounds []int64 `protobuf:"varint,1,rep,name=legacyBounds" json:"legacyBounds,omitempty"` + Bounds []string `protobuf:"bytes,2,rep,name=bounds" json:"bounds,omitempty"` } func (m *Axis) Reset() { *m = Axis{} } @@ -312,64 +313,65 @@ func init() { func init() { proto.RegisterFile("internal.proto", fileDescriptorInternal) } var fileDescriptorInternal = []byte{ - // 935 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xbc, 0x56, 0xdd, 0x8e, 0xdb, 0x44, - 0x14, 0xd6, 0xc4, 0x76, 0x12, 0x9f, 0x6d, 0x17, 0x34, 0xaa, 0xa8, 0x29, 0x12, 0x0a, 0x16, 0x48, - 0x01, 0x89, 0x05, 0xb5, 0x42, 0x42, 0xdc, 0x65, 0x37, 0xa8, 0x0a, 0xbb, 0x2d, 0xcb, 0x64, 0x77, - 0xb9, 0x42, 0xd5, 0x24, 0x39, 0xd9, 0xb5, 0xea, 0xd8, 0x66, 0x6c, 0x6f, 0xe2, 0x57, 0xe0, 0x8a, - 0x27, 0x40, 0x42, 0xe2, 0x8a, 0x4b, 0x5e, 0x80, 0x87, 0xe0, 0x85, 0xd0, 0x99, 0x19, 0xff, 0x84, - 0x6e, 0x51, 0xaf, 0x7a, 0x37, 0xdf, 0x39, 0x93, 0xef, 0x78, 0xbe, 0xf3, 0x9d, 0xa3, 0xc0, 0x61, - 0x94, 0x14, 0xa8, 0x12, 0x19, 0x1f, 0x65, 0x2a, 0x2d, 0x52, 0x3e, 0xac, 0x71, 0xf8, 0x4b, 0x0f, - 0xfa, 0xf3, 0xb4, 0x54, 0x4b, 0xe4, 0x87, 0xd0, 0x9b, 0x4d, 0x03, 0x36, 0x62, 0x63, 0x47, 0xf4, - 0x66, 0x53, 0xce, 0xc1, 0x7d, 0x2e, 0x37, 0x18, 0xf4, 0x46, 0x6c, 0xec, 0x0b, 0x7d, 0xa6, 0xd8, - 0x45, 0x95, 0x61, 0xe0, 0x98, 0x18, 0x9d, 0xf9, 0x23, 0x18, 0x5e, 0xe6, 0xc4, 0xb6, 0xc1, 0xc0, - 0xd5, 0xf1, 0x06, 0x53, 0xee, 0x5c, 0xe6, 0xf9, 0x36, 0x55, 0xab, 0xc0, 0x33, 0xb9, 0x1a, 0xf3, - 0x77, 0xc1, 0xb9, 0x14, 0x67, 0x41, 0x5f, 0x87, 0xe9, 0xc8, 0x03, 0x18, 0x4c, 0x71, 0x2d, 0xcb, - 0xb8, 0x08, 0x06, 0x23, 0x36, 0x1e, 0x8a, 0x1a, 0x12, 0xcf, 0x05, 0xc6, 0x78, 0xad, 0xe4, 0x3a, - 0x18, 0x1a, 0x9e, 0x1a, 0xf3, 0x23, 0xe0, 0xb3, 0x24, 0xc7, 0x65, 0xa9, 0x70, 0xfe, 0x32, 0xca, - 0xae, 0x50, 0x45, 0xeb, 0x2a, 0xf0, 0x35, 0xc1, 0x1d, 0x19, 0xaa, 0xf2, 0x0c, 0x0b, 0x49, 0xb5, - 0x41, 0x53, 0xd5, 0x30, 0xfc, 0x95, 0x81, 0x3f, 0x95, 0xf9, 0xcd, 0x22, 0x95, 0x6a, 0xf5, 0x46, - 0x7a, 0x7c, 0x0e, 0xde, 0x12, 0xe3, 0x38, 0x0f, 0x9c, 0x91, 0x33, 0x3e, 0x78, 0xfc, 0xf0, 0xa8, - 0x11, 0xba, 0xe1, 0x39, 0xc1, 0x38, 0x16, 0xe6, 0x16, 0xff, 0x12, 0xfc, 0x02, 0x37, 0x59, 0x2c, - 0x0b, 0xcc, 0x03, 0x57, 0xff, 0x84, 0xb7, 0x3f, 0xb9, 0xb0, 0x29, 0xd1, 0x5e, 0x0a, 0xff, 0xec, - 0xc1, 0xfd, 0x3d, 0x2a, 0x7e, 0x0f, 0xd8, 0x4e, 0x7f, 0x95, 0x27, 0xd8, 0x8e, 0x50, 0xa5, 0xbf, - 0xc8, 0x13, 0xac, 0x22, 0xb4, 0xd5, 0xbd, 0xf1, 0x04, 0xdb, 0x12, 0xba, 0xd1, 0x1d, 0xf1, 0x04, - 0xbb, 0xe1, 0x9f, 0xc2, 0xe0, 0xe7, 0x12, 0x55, 0x84, 0x79, 0xe0, 0xe9, 0xca, 0xef, 0xb4, 0x95, - 0x7f, 0x28, 0x51, 0x55, 0xa2, 0xce, 0xd3, 0x4b, 0x75, 0x37, 0x4d, 0x6b, 0xf4, 0x99, 0x62, 0x05, - 0x75, 0x7e, 0x60, 0x62, 0x74, 0xb6, 0x0a, 0x99, 0x7e, 0x90, 0x42, 0x5f, 0x81, 0x2b, 0x77, 0x98, - 0x07, 0xbe, 0xe6, 0xff, 0xe8, 0x35, 0x62, 0x1c, 0x4d, 0x76, 0x98, 0x7f, 0x9b, 0x14, 0xaa, 0x12, - 0xfa, 0xfa, 0xa3, 0xa7, 0xe0, 0x37, 0x21, 0x72, 0xc5, 0x4b, 0xac, 0xf4, 0x03, 0x7d, 0x41, 0x47, - 0xfe, 0x31, 0x78, 0xb7, 0x32, 0x2e, 0x8d, 0xf0, 0x07, 0x8f, 0x0f, 0x5b, 0xda, 0xc9, 0x2e, 0xca, - 0x85, 0x49, 0x7e, 0xd3, 0xfb, 0x9a, 0x85, 0x1f, 0x82, 0x4b, 0x21, 0xfe, 0x1e, 0xf4, 0x17, 0x69, - 0x99, 0xac, 0xf2, 0x80, 0x8d, 0x9c, 0xb1, 0x23, 0x2c, 0x0a, 0xff, 0x66, 0x64, 0x23, 0x23, 0x6d, - 0xa7, 0xbd, 0xe6, 0xe3, 0xdf, 0x87, 0x21, 0xc9, 0xfe, 0xe2, 0x56, 0x2a, 0xdb, 0xe2, 0x01, 0xe1, - 0x2b, 0xa9, 0xf8, 0x17, 0xd0, 0xd7, 0x45, 0xee, 0x68, 0x73, 0x4d, 0x77, 0x45, 0x79, 0x61, 0xaf, - 0x35, 0x62, 0xb9, 0x1d, 0xb1, 0x1e, 0x80, 0x17, 0xcb, 0x05, 0xc6, 0x76, 0x0e, 0x0c, 0x20, 0x03, - 0x91, 0xea, 0x95, 0xd6, 0xfa, 0x4e, 0x66, 0xd3, 0x1b, 0x73, 0x2b, 0xbc, 0x84, 0xfb, 0x7b, 0x15, - 0x9b, 0x4a, 0x6c, 0xbf, 0x52, 0x2b, 0x98, 0x6f, 0x05, 0xa2, 0x11, 0xca, 0x31, 0xc6, 0x65, 0x81, - 0x2b, 0x6d, 0x91, 0xa1, 0x68, 0x70, 0xf8, 0x3b, 0x6b, 0x79, 0x75, 0x3d, 0x1a, 0x92, 0x65, 0xba, - 0xd9, 0xc8, 0x64, 0x65, 0xa9, 0x6b, 0x48, 0xba, 0xad, 0x16, 0x96, 0xba, 0xb7, 0x5a, 0x10, 0x56, - 0x99, 0x5d, 0x08, 0x3d, 0x95, 0xf1, 0x11, 0x1c, 0x6c, 0x50, 0xe6, 0xa5, 0xc2, 0x0d, 0x26, 0x85, - 0x95, 0xa0, 0x1b, 0xe2, 0x0f, 0x61, 0x50, 0xc8, 0xeb, 0x17, 0xd4, 0x66, 0xa3, 0x45, 0xbf, 0x90, - 0xd7, 0xa7, 0x58, 0xf1, 0x0f, 0xc0, 0x5f, 0x47, 0x18, 0xaf, 0x74, 0xca, 0x98, 0x6f, 0xa8, 0x03, - 0xa7, 0x58, 0x85, 0x7f, 0x30, 0xe8, 0xcf, 0x51, 0xdd, 0xa2, 0x7a, 0xa3, 0xc9, 0xec, 0x6e, 0x25, - 0xe7, 0x7f, 0xb6, 0x92, 0x7b, 0xf7, 0x56, 0xf2, 0xda, 0xad, 0xf4, 0x00, 0xbc, 0xb9, 0x5a, 0xce, - 0xa6, 0xfa, 0x8b, 0x1c, 0x61, 0x00, 0x79, 0x6c, 0xb2, 0x2c, 0xa2, 0x5b, 0xb4, 0xab, 0xca, 0xa2, - 0xf0, 0x37, 0x06, 0xfd, 0x33, 0x59, 0xa5, 0x65, 0xf1, 0x8a, 0xc3, 0x46, 0x70, 0x30, 0xc9, 0xb2, - 0x38, 0x5a, 0xca, 0x22, 0x4a, 0x13, 0xfb, 0xb5, 0xdd, 0x10, 0xdd, 0x78, 0xd6, 0xd1, 0xce, 0x7c, - 0x77, 0x37, 0x44, 0xc3, 0x70, 0xa2, 0x17, 0x8e, 0xd9, 0x1e, 0x9d, 0x61, 0x30, 0x7b, 0x46, 0x27, - 0xe9, 0x81, 0x93, 0xb2, 0x48, 0xd7, 0x71, 0xba, 0xd5, 0x2f, 0x19, 0x8a, 0x06, 0x87, 0xff, 0x30, - 0x70, 0xdf, 0xd6, 0x22, 0xb9, 0x07, 0x2c, 0xb2, 0x8d, 0x64, 0x51, 0xb3, 0x56, 0x06, 0x9d, 0xb5, - 0x12, 0xc0, 0xa0, 0x52, 0x32, 0xb9, 0xc6, 0x3c, 0x18, 0xea, 0x59, 0xad, 0xa1, 0xce, 0xe8, 0x19, - 0x31, 0xfb, 0xc4, 0x17, 0x35, 0x6c, 0x3c, 0x0f, 0xad, 0xe7, 0xc3, 0xbf, 0x18, 0x78, 0x8d, 0x73, - 0x4f, 0xf6, 0x9d, 0x7b, 0xd2, 0x3a, 0x77, 0x7a, 0x5c, 0x3b, 0x77, 0x7a, 0x4c, 0x58, 0x9c, 0xd7, - 0xce, 0x15, 0xe7, 0xa4, 0xda, 0x53, 0x95, 0x96, 0xd9, 0x71, 0x65, 0xe4, 0xf5, 0x45, 0x83, 0xa9, - 0xdd, 0x3f, 0xde, 0xa0, 0xb2, 0x6f, 0xf6, 0x85, 0x45, 0x64, 0x8e, 0x33, 0x3d, 0xd5, 0xe6, 0x95, - 0x06, 0xf0, 0x4f, 0xc0, 0x13, 0xf4, 0x0a, 0xfd, 0xd4, 0x3d, 0x81, 0x74, 0x58, 0x98, 0x6c, 0xf8, - 0xc4, 0x5e, 0x23, 0x96, 0xcb, 0x2c, 0x43, 0x65, 0x3d, 0x6d, 0x80, 0xe6, 0x4e, 0xb7, 0x68, 0xd6, - 0x91, 0x23, 0x0c, 0x08, 0x7f, 0x02, 0x7f, 0x12, 0xa3, 0x2a, 0x44, 0x19, 0xbf, 0xba, 0xc4, 0x38, - 0xb8, 0xdf, 0xcd, 0xbf, 0x7f, 0x5e, 0x4f, 0x02, 0x9d, 0x5b, 0xff, 0x3a, 0xff, 0xf1, 0xef, 0xa9, - 0xcc, 0xe4, 0x6c, 0xaa, 0x1b, 0xeb, 0x08, 0x8b, 0xc2, 0xcf, 0xc0, 0xa5, 0x39, 0xe9, 0x30, 0xbb, - 0xaf, 0x9b, 0xb1, 0x45, 0x5f, 0xff, 0x9b, 0x78, 0xf2, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa1, - 0xf4, 0x45, 0x04, 0x5f, 0x08, 0x00, 0x00, + // 950 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xbc, 0x56, 0xcd, 0x8e, 0xe3, 0x44, + 0x10, 0x56, 0xc7, 0x76, 0x12, 0xd7, 0xcc, 0x0e, 0xa8, 0xb5, 0x62, 0xcd, 0x72, 0x09, 0x16, 0x48, + 0x01, 0x89, 0x01, 0xed, 0x0a, 0x09, 0x71, 0x4b, 0x26, 0x68, 0x15, 0x66, 0x76, 0x19, 0x3a, 0x33, + 0xc3, 0x09, 0xad, 0x3a, 0x49, 0x25, 0x63, 0xad, 0x13, 0x9b, 0xb6, 0x3d, 0x89, 0x5f, 0x81, 0x13, + 0x4f, 0x80, 0x84, 0xc4, 0x89, 0x23, 0x2f, 0xc0, 0x43, 0xf0, 0x42, 0xa8, 0xba, 0xdb, 0x3f, 0x61, + 0x67, 0xd1, 0x9e, 0xb8, 0xf5, 0x57, 0xd5, 0xf9, 0xda, 0xf5, 0xd5, 0x57, 0xa5, 0xc0, 0x49, 0xb4, + 0xcd, 0x51, 0x6d, 0x65, 0x7c, 0x9a, 0xaa, 0x24, 0x4f, 0x78, 0xbf, 0xc2, 0xe1, 0xcf, 0x1d, 0xe8, + 0xce, 0x92, 0x42, 0x2d, 0x90, 0x9f, 0x40, 0x67, 0x3a, 0x09, 0xd8, 0x80, 0x0d, 0x1d, 0xd1, 0x99, + 0x4e, 0x38, 0x07, 0xf7, 0x85, 0xdc, 0x60, 0xd0, 0x19, 0xb0, 0xa1, 0x2f, 0xf4, 0x99, 0x62, 0x57, + 0x65, 0x8a, 0x81, 0x63, 0x62, 0x74, 0xe6, 0x8f, 0xa1, 0x7f, 0x9d, 0x11, 0xdb, 0x06, 0x03, 0x57, + 0xc7, 0x6b, 0x4c, 0xb9, 0x4b, 0x99, 0x65, 0xbb, 0x44, 0x2d, 0x03, 0xcf, 0xe4, 0x2a, 0xcc, 0xdf, + 0x05, 0xe7, 0x5a, 0x5c, 0x04, 0x5d, 0x1d, 0xa6, 0x23, 0x0f, 0xa0, 0x37, 0xc1, 0x95, 0x2c, 0xe2, + 0x3c, 0xe8, 0x0d, 0xd8, 0xb0, 0x2f, 0x2a, 0x48, 0x3c, 0x57, 0x18, 0xe3, 0x5a, 0xc9, 0x55, 0xd0, + 0x37, 0x3c, 0x15, 0xe6, 0xa7, 0xc0, 0xa7, 0xdb, 0x0c, 0x17, 0x85, 0xc2, 0xd9, 0xab, 0x28, 0xbd, + 0x41, 0x15, 0xad, 0xca, 0xc0, 0xd7, 0x04, 0xf7, 0x64, 0xe8, 0x95, 0xe7, 0x98, 0x4b, 0x7a, 0x1b, + 0x34, 0x55, 0x05, 0xc3, 0x5f, 0x18, 0xf8, 0x13, 0x99, 0xdd, 0xce, 0x13, 0xa9, 0x96, 0x6f, 0xa5, + 0xc7, 0x67, 0xe0, 0x2d, 0x30, 0x8e, 0xb3, 0xc0, 0x19, 0x38, 0xc3, 0xa3, 0x27, 0x8f, 0x4e, 0x6b, + 0xa1, 0x6b, 0x9e, 0x33, 0x8c, 0x63, 0x61, 0x6e, 0xf1, 0x2f, 0xc0, 0xcf, 0x71, 0x93, 0xc6, 0x32, + 0xc7, 0x2c, 0x70, 0xf5, 0x4f, 0x78, 0xf3, 0x93, 0x2b, 0x9b, 0x12, 0xcd, 0xa5, 0xf0, 0x8f, 0x0e, + 0x3c, 0x38, 0xa0, 0xe2, 0xc7, 0xc0, 0xf6, 0xfa, 0xab, 0x3c, 0xc1, 0xf6, 0x84, 0x4a, 0xfd, 0x45, + 0x9e, 0x60, 0x25, 0xa1, 0x9d, 0xee, 0x8d, 0x27, 0xd8, 0x8e, 0xd0, 0xad, 0xee, 0x88, 0x27, 0xd8, + 0x2d, 0xff, 0x04, 0x7a, 0x3f, 0x15, 0xa8, 0x22, 0xcc, 0x02, 0x4f, 0xbf, 0xfc, 0x4e, 0xf3, 0xf2, + 0xf7, 0x05, 0xaa, 0x52, 0x54, 0x79, 0xaa, 0x54, 0x77, 0xd3, 0xb4, 0x46, 0x9f, 0x29, 0x96, 0x53, + 0xe7, 0x7b, 0x26, 0x46, 0x67, 0xab, 0x90, 0xe9, 0x07, 0x29, 0xf4, 0x25, 0xb8, 0x72, 0x8f, 0x59, + 0xe0, 0x6b, 0xfe, 0x0f, 0xdf, 0x20, 0xc6, 0xe9, 0x68, 0x8f, 0xd9, 0x37, 0xdb, 0x5c, 0x95, 0x42, + 0x5f, 0x7f, 0xfc, 0x0c, 0xfc, 0x3a, 0x44, 0xae, 0x78, 0x85, 0xa5, 0x2e, 0xd0, 0x17, 0x74, 0xe4, + 0x1f, 0x81, 0x77, 0x27, 0xe3, 0xc2, 0x08, 0x7f, 0xf4, 0xe4, 0xa4, 0xa1, 0x1d, 0xed, 0xa3, 0x4c, + 0x98, 0xe4, 0xd7, 0x9d, 0xaf, 0x58, 0x38, 0x06, 0x97, 0x42, 0x3c, 0x84, 0xe3, 0x18, 0xd7, 0x72, + 0x51, 0x8e, 0x93, 0x62, 0xbb, 0xcc, 0x02, 0x36, 0x70, 0x86, 0x8e, 0x38, 0x88, 0xf1, 0xf7, 0xa0, + 0x3b, 0x37, 0xd9, 0xce, 0xc0, 0x19, 0xfa, 0xc2, 0xa2, 0xf0, 0x2f, 0x46, 0x56, 0x33, 0xf2, 0xb7, + 0x2c, 0x60, 0x0a, 0x7c, 0x1f, 0xfa, 0xd4, 0x9a, 0x97, 0x77, 0x52, 0x59, 0x1b, 0xf4, 0x08, 0xdf, + 0x48, 0xc5, 0x3f, 0x87, 0xae, 0xfe, 0x90, 0x7b, 0xac, 0x50, 0xd1, 0xdd, 0x50, 0x5e, 0xd8, 0x6b, + 0xb5, 0xa0, 0x6e, 0x4b, 0xd0, 0x87, 0xe0, 0xc5, 0x72, 0x8e, 0xb1, 0x9d, 0x15, 0x03, 0xc8, 0x64, + 0xd4, 0x99, 0x52, 0xf7, 0xe3, 0x5e, 0x66, 0xd3, 0x3f, 0x73, 0x2b, 0xbc, 0x86, 0x07, 0x07, 0x2f, + 0xd6, 0x2f, 0xb1, 0xc3, 0x97, 0x1a, 0x51, 0x7d, 0x2b, 0x22, 0x8d, 0x59, 0x86, 0x31, 0x2e, 0x72, + 0x5c, 0x6a, 0x1b, 0xf5, 0x45, 0x8d, 0xc3, 0xdf, 0x58, 0xc3, 0xab, 0xdf, 0xa3, 0x41, 0x5a, 0x24, + 0x9b, 0x8d, 0xdc, 0x2e, 0x2d, 0x75, 0x05, 0x49, 0xb7, 0xe5, 0xdc, 0x52, 0x77, 0x96, 0x73, 0xc2, + 0x2a, 0xb5, 0x4b, 0xa3, 0xa3, 0x52, 0x3e, 0x80, 0xa3, 0x0d, 0xca, 0xac, 0x50, 0xb8, 0xc1, 0x6d, + 0x6e, 0x25, 0x68, 0x87, 0xf8, 0x23, 0xe8, 0xe5, 0x72, 0xfd, 0x92, 0xac, 0x60, 0xb4, 0xe8, 0xe6, + 0x72, 0x7d, 0x8e, 0x25, 0xff, 0x00, 0xfc, 0x55, 0x84, 0xf1, 0x52, 0xa7, 0x8c, 0x41, 0xfb, 0x3a, + 0x70, 0x8e, 0x65, 0xf8, 0x3b, 0x83, 0xee, 0x0c, 0xd5, 0x1d, 0xaa, 0xb7, 0x9a, 0xde, 0xf6, 0xe6, + 0x72, 0xfe, 0x63, 0x73, 0xb9, 0xf7, 0x6f, 0x2e, 0xaf, 0xd9, 0x5c, 0x0f, 0xc1, 0x9b, 0xa9, 0xc5, + 0x74, 0xa2, 0xbf, 0xc8, 0x11, 0x06, 0x90, 0xc7, 0x46, 0x8b, 0x3c, 0xba, 0x43, 0xbb, 0xce, 0x2c, + 0x0a, 0x7f, 0x65, 0xd0, 0xbd, 0x90, 0x65, 0x52, 0xe4, 0xaf, 0x39, 0x6c, 0x00, 0x47, 0xa3, 0x34, + 0x8d, 0xa3, 0x85, 0xcc, 0xa3, 0x64, 0x6b, 0xbf, 0xb6, 0x1d, 0xa2, 0x1b, 0xcf, 0x5b, 0xda, 0x99, + 0xef, 0x6e, 0x87, 0x68, 0x60, 0xce, 0xf4, 0x52, 0x32, 0x1b, 0xa6, 0x35, 0x30, 0x66, 0x17, 0xe9, + 0x24, 0x15, 0x38, 0x2a, 0xf2, 0x64, 0x15, 0x27, 0x3b, 0x5d, 0x49, 0x5f, 0xd4, 0x38, 0xfc, 0x9b, + 0x81, 0xfb, 0x7f, 0x2d, 0x9b, 0x63, 0x60, 0x91, 0x6d, 0x24, 0x8b, 0xea, 0xd5, 0xd3, 0x6b, 0xad, + 0x9e, 0x00, 0x7a, 0xa5, 0x92, 0xdb, 0x35, 0x66, 0x41, 0x5f, 0x4f, 0x72, 0x05, 0x75, 0x46, 0xcf, + 0x88, 0xd9, 0x39, 0xbe, 0xa8, 0x60, 0xed, 0x79, 0x68, 0x3c, 0x1f, 0xfe, 0xc9, 0xc0, 0xab, 0x9d, + 0x7b, 0x76, 0xe8, 0xdc, 0xb3, 0xc6, 0xb9, 0x93, 0x71, 0xe5, 0xdc, 0xc9, 0x98, 0xb0, 0xb8, 0xac, + 0x9c, 0x2b, 0x2e, 0x49, 0xb5, 0x67, 0x2a, 0x29, 0xd2, 0x71, 0x69, 0xe4, 0xf5, 0x45, 0x8d, 0xa9, + 0xdd, 0x3f, 0xdc, 0xa2, 0xb2, 0x35, 0xfb, 0xc2, 0x22, 0x32, 0xc7, 0x85, 0x9e, 0x6a, 0x53, 0xa5, + 0x01, 0xfc, 0x63, 0xf0, 0x04, 0x55, 0xa1, 0x4b, 0x3d, 0x10, 0x48, 0x87, 0x85, 0xc9, 0x86, 0x4f, + 0xed, 0x35, 0x62, 0xb9, 0x4e, 0x53, 0x54, 0xd6, 0xd3, 0x06, 0x68, 0xee, 0x64, 0x87, 0x66, 0x1d, + 0x39, 0xc2, 0x80, 0xf0, 0x47, 0xf0, 0x47, 0x31, 0xaa, 0x5c, 0x14, 0xf1, 0xeb, 0x4b, 0x8c, 0x83, + 0xfb, 0xed, 0xec, 0xbb, 0x17, 0xd5, 0x24, 0xd0, 0xb9, 0xf1, 0xaf, 0xf3, 0x2f, 0xff, 0x9e, 0xcb, + 0x54, 0x4e, 0x27, 0xba, 0xb1, 0x8e, 0xb0, 0x28, 0xfc, 0x14, 0x5c, 0x9a, 0x93, 0x16, 0xb3, 0xfb, + 0xa6, 0x19, 0x9b, 0x77, 0xf5, 0x3f, 0x8e, 0xa7, 0xff, 0x04, 0x00, 0x00, 0xff, 0xff, 0x33, 0xaf, + 0xb8, 0xb8, 0x83, 0x08, 0x00, 0x00, } diff --git a/bolt/internal/internal.proto b/bolt/internal/internal.proto index 2b5981363a..18a2a77468 100644 --- a/bolt/internal/internal.proto +++ b/bolt/internal/internal.proto @@ -34,7 +34,8 @@ message DashboardCell { } message Axis { - repeated int64 bounds = 1; // bounds are an ordered 2-tuple consisting of lower and upper axis extents, respectively + repeated int64 legacyBounds = 1; // legacyBounds are an ordered 2-tuple consisting of lower and upper axis extents, respectively + repeated string bounds = 2; // bounds are an arbitrary list of client-defined bounds. } message Template { diff --git a/bolt/internal/internal_test.go b/bolt/internal/internal_test.go index 5c0a3bfd9e..7eb86ac308 100644 --- a/bolt/internal/internal_test.go +++ b/bolt/internal/internal_test.go @@ -128,7 +128,7 @@ func Test_MarshalDashboard(t *testing.T) { }, Axes: map[string]chronograf.Axis{ "y": chronograf.Axis{ - Bounds: [2]int64{0, 100}, + Bounds: []string{"0", "3", "1-7", "foo"}, }, }, Type: "line", @@ -147,3 +147,76 @@ func Test_MarshalDashboard(t *testing.T) { t.Fatalf("Dashboard protobuf copy error: diff follows:\n%s", cmp.Diff(dashboard, actual)) } } + +func Test_MarshalDashboard_WithLegacyBounds(t *testing.T) { + dashboard := chronograf.Dashboard{ + ID: 1, + Cells: []chronograf.DashboardCell{ + { + ID: "9b5367de-c552-4322-a9e8-7f384cbd235c", + X: 0, + Y: 0, + W: 4, + H: 4, + Name: "Super awesome query", + Queries: []chronograf.DashboardQuery{ + { + Command: "select * from cpu", + Label: "CPU Utilization", + Range: &chronograf.Range{ + Upper: int64(100), + }, + }, + }, + Axes: map[string]chronograf.Axis{ + "y": chronograf.Axis{ + LegacyBounds: [2]int64{0, 5}, + }, + }, + Type: "line", + }, + }, + Templates: []chronograf.Template{}, + Name: "Dashboard", + } + + expected := chronograf.Dashboard{ + ID: 1, + Cells: []chronograf.DashboardCell{ + { + ID: "9b5367de-c552-4322-a9e8-7f384cbd235c", + X: 0, + Y: 0, + W: 4, + H: 4, + Name: "Super awesome query", + Queries: []chronograf.DashboardQuery{ + { + Command: "select * from cpu", + Label: "CPU Utilization", + Range: &chronograf.Range{ + Upper: int64(100), + }, + }, + }, + Axes: map[string]chronograf.Axis{ + "y": chronograf.Axis{ + Bounds: []string{"0", "5"}, + }, + }, + Type: "line", + }, + }, + Templates: []chronograf.Template{}, + Name: "Dashboard", + } + + var actual chronograf.Dashboard + if buf, err := internal.MarshalDashboard(dashboard); err != nil { + t.Fatal("Error marshaling dashboard: err", err) + } else if err := internal.UnmarshalDashboard(buf, &actual); err != nil { + t.Fatal("Error unmarshaling dashboard: err:", err) + } else if !cmp.Equal(expected, actual) { + t.Fatalf("Dashboard protobuf copy error: diff follows:\n%s", cmp.Diff(expected, actual)) + } +} diff --git a/chronograf.go b/chronograf.go index 8a62763ab3..b8dcd4537c 100644 --- a/chronograf.go +++ b/chronograf.go @@ -568,7 +568,8 @@ type Dashboard struct { // Axis represents the visible extents of a visualization type Axis struct { - Bounds [2]int64 `json:"bounds"` // bounds are an ordered 2-tuple consisting of lower and upper axis extents, respectively + Bounds []string `json:"bounds"` // bounds are an arbitrary list of client-defined strings that specify the viewport for a cell + LegacyBounds [2]int64 `json:"-"` // legacy bounds are for testing a migration from an earlier version of axis } // DashboardCell holds visual and query information for a cell diff --git a/server/cells_test.go b/server/cells_test.go index be694b3dff..a3ae00e9c1 100644 --- a/server/cells_test.go +++ b/server/cells_test.go @@ -20,13 +20,13 @@ func Test_Cells_CorrectAxis(t *testing.T) { &chronograf.DashboardCell{ Axes: map[string]chronograf.Axis{ "x": chronograf.Axis{ - Bounds: [2]int64{0, 100}, + Bounds: []string{"0", "100"}, }, "y": chronograf.Axis{ - Bounds: [2]int64{0, 100}, + Bounds: []string{"0", "100"}, }, "y2": chronograf.Axis{ - Bounds: [2]int64{0, 100}, + Bounds: []string{"0", "100"}, }, }, }, @@ -37,10 +37,10 @@ func Test_Cells_CorrectAxis(t *testing.T) { &chronograf.DashboardCell{ Axes: map[string]chronograf.Axis{ "axis of evil": chronograf.Axis{ - Bounds: [2]int64{666, 666}, + Bounds: []string{"666", "666"}, }, "axis of awesome": chronograf.Axis{ - Bounds: [2]int64{1337, 31337}, + Bounds: []string{"1337", "31337"}, }, }, }, diff --git a/server/dashboards_test.go b/server/dashboards_test.go index b5116913c5..5c92083451 100644 --- a/server/dashboards_test.go +++ b/server/dashboards_test.go @@ -222,10 +222,10 @@ func Test_newDashboardResponse(t *testing.T) { }, Axes: map[string]chronograf.Axis{ "x": chronograf.Axis{ - Bounds: [2]int64{0, 100}, + Bounds: []string{"0", "100"}, }, "y": chronograf.Axis{ - Bounds: [2]int64{2, 95}, + Bounds: []string{"2", "95"}, }, }, }, @@ -268,10 +268,10 @@ func Test_newDashboardResponse(t *testing.T) { }, Axes: map[string]chronograf.Axis{ "x": chronograf.Axis{ - Bounds: [2]int64{0, 100}, + Bounds: []string{"0", "100"}, }, "y": chronograf.Axis{ - Bounds: [2]int64{2, 95}, + Bounds: []string{"2", "95"}, }, }, }, From 4391004e7fcced14de426ed7c00e753ff2be9dc3 Mon Sep 17 00:00:00 2001 From: Tim Raymond Date: Thu, 27 Jul 2017 15:57:04 -0400 Subject: [PATCH 37/93] Enforce presence of "x", "y", and "y2" axes Certain aspects of the frontend requires the presence of these three axes, so part of the contract established is that the backend will always provide them. Since we centralize creation of dashboardCellResponses, this is where these axes are added to all cell responses. Additionally, because there was previously no coverage over the dashboard cells endpoints, a test has been added to cover the DashboardCells method of Service. --- mocks/dashboards.go | 37 ++++++++++ mocks/logger.go | 9 +++ server/cells.go | 14 ++++ server/cells_test.go | 137 ++++++++++++++++++++++++++++++++++++++ server/dashboards_test.go | 6 ++ 5 files changed, 203 insertions(+) create mode 100644 mocks/dashboards.go diff --git a/mocks/dashboards.go b/mocks/dashboards.go new file mode 100644 index 0000000000..a038f468ba --- /dev/null +++ b/mocks/dashboards.go @@ -0,0 +1,37 @@ +package mocks + +import ( + "context" + + "github.com/influxdata/chronograf" +) + +var _ chronograf.DashboardsStore = &DashboardsStore{} + +type DashboardsStore struct { + AddF func(ctx context.Context, newDashboard chronograf.Dashboard) (chronograf.Dashboard, error) + AllF func(ctx context.Context) ([]chronograf.Dashboard, error) + DeleteF func(ctx context.Context, target chronograf.Dashboard) error + GetF func(ctx context.Context, id chronograf.DashboardID) (chronograf.Dashboard, error) + UpdateF func(ctx context.Context, target chronograf.Dashboard) error +} + +func (d *DashboardsStore) Add(ctx context.Context, newDashboard chronograf.Dashboard) (chronograf.Dashboard, error) { + return d.AddF(ctx, newDashboard) +} + +func (d *DashboardsStore) All(ctx context.Context) ([]chronograf.Dashboard, error) { + return d.AllF(ctx) +} + +func (d *DashboardsStore) Delete(ctx context.Context, target chronograf.Dashboard) error { + return d.DeleteF(ctx, target) +} + +func (d *DashboardsStore) Get(ctx context.Context, id chronograf.DashboardID) (chronograf.Dashboard, error) { + return d.GetF(ctx, id) +} + +func (d *DashboardsStore) Update(ctx context.Context, target chronograf.Dashboard) error { + return d.UpdateF(ctx, target) +} diff --git a/mocks/logger.go b/mocks/logger.go index 46c7bae9f6..f2926e09a3 100644 --- a/mocks/logger.go +++ b/mocks/logger.go @@ -3,6 +3,7 @@ package mocks import ( "fmt" "io" + "testing" "github.com/influxdata/chronograf" ) @@ -72,3 +73,11 @@ func (tl *TestLogger) stringifyArg(arg interface{}) []byte { return []byte("UNKNOWN") } } + +// Dump dumps out logs into a given testing.T's logs +func (tl *TestLogger) Dump(t *testing.T) { + t.Log("== Dumping Test Logs ==") + for _, msg := range tl.Messages { + t.Logf("lvl: %s, msg: %s", msg.Level, msg.Body) + } +} diff --git a/server/cells.go b/server/cells.go index f60437c910..a16cb702d5 100644 --- a/server/cells.go +++ b/server/cells.go @@ -33,6 +33,20 @@ func newCellResponses(dID chronograf.DashboardID, dcells []chronograf.DashboardC if len(cell.Queries) == 0 { cell.Queries = make([]chronograf.DashboardQuery, 0) } + + // ensure x, y, and y2 axes always returned + labels := []string{"x", "y", "y2"} + if cell.Axes == nil { + cell.Axes = make(map[string]chronograf.Axis, len(labels)) + } + + for _, lbl := range labels { + _, found := cell.Axes[lbl] + if !found { + cell.Axes[lbl] = chronograf.Axis{} + } + } + cells[i] = dashboardCellResponse{ DashboardCell: cell, Links: dashboardCellLinks{ diff --git a/server/cells_test.go b/server/cells_test.go index a3ae00e9c1..1be5981f9f 100644 --- a/server/cells_test.go +++ b/server/cells_test.go @@ -1,9 +1,18 @@ package server_test import ( + "context" + "encoding/json" + "net/http" + "net/http/httptest" + "net/url" + "strings" "testing" + "github.com/bouk/httprouter" + "github.com/google/go-cmp/cmp" "github.com/influxdata/chronograf" + "github.com/influxdata/chronograf/mocks" "github.com/influxdata/chronograf/server" ) @@ -58,3 +67,131 @@ func Test_Cells_CorrectAxis(t *testing.T) { }) } } + +func Test_Service_DashboardCells(t *testing.T) { + cellsTests := []struct { + name string + reqURL *url.URL + ctxParams map[string]string + mockResponse []chronograf.DashboardCell + expected []chronograf.DashboardCell + expectedCode int + }{ + { + "happy path", + &url.URL{ + Path: "/chronograf/v1/dashboards/1/cells", + }, + map[string]string{ + "id": "1", + }, + []chronograf.DashboardCell{}, + []chronograf.DashboardCell{}, + http.StatusOK, + }, + { + "cell axes should always be \"x\", \"y\", and \"y2\"", + &url.URL{ + Path: "/chronograf/v1/dashboards/1/cells", + }, + map[string]string{ + "id": "1", + }, + []chronograf.DashboardCell{ + { + ID: "3899be5a-f6eb-4347-b949-de2f4fbea859", + X: 0, + Y: 0, + W: 4, + H: 4, + Name: "CPU", + Queries: []chronograf.DashboardQuery{}, + Axes: map[string]chronograf.Axis{}, + }, + }, + []chronograf.DashboardCell{ + { + ID: "3899be5a-f6eb-4347-b949-de2f4fbea859", + X: 0, + Y: 0, + W: 4, + H: 4, + Name: "CPU", + Queries: []chronograf.DashboardQuery{}, + Axes: map[string]chronograf.Axis{ + "x": chronograf.Axis{}, + "y": chronograf.Axis{}, + "y2": chronograf.Axis{}, + }, + }, + }, + http.StatusOK, + }, + } + + for _, test := range cellsTests { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + // setup context with params + ctx := context.Background() + params := httprouter.Params{} + for k, v := range test.ctxParams { + params = append(params, httprouter.Param{k, v}) + } + ctx = httprouter.WithParams(ctx, params) + + // setup response recorder and request + rr := httptest.NewRecorder() + req := httptest.NewRequest("GET", test.reqURL.RequestURI(), strings.NewReader("")).WithContext(ctx) + + // setup mock DashboardCells store and logger + tlog := &mocks.TestLogger{} + svc := &server.Service{ + DashboardsStore: &mocks.DashboardsStore{ + GetF: func(ctx context.Context, id chronograf.DashboardID) (chronograf.Dashboard, error) { + return chronograf.Dashboard{ + ID: chronograf.DashboardID(1), + Cells: test.mockResponse, + Templates: []chronograf.Template{}, + Name: "empty dashboard", + }, nil + }, + }, + Logger: tlog, + } + + // invoke DashboardCell handler + svc.DashboardCells(rr, req) + + // setup frame to decode response into + respFrame := []struct { + chronograf.DashboardCell + Links json.RawMessage `json:"links"` // ignore links + }{} + + // decode response + resp := rr.Result() + + if resp.StatusCode != test.expectedCode { + tlog.Dump(t) + t.Fatalf("%q - Status codes do not match. Want %d (%s), Got %d (%s)", test.name, test.expectedCode, http.StatusText(test.expectedCode), resp.StatusCode, http.StatusText(resp.StatusCode)) + } + + if err := json.NewDecoder(resp.Body).Decode(&respFrame); err != nil { + t.Fatalf("%q - Error unmarshaling response body: err: %s", test.name, err) + } + + // extract actual + actual := []chronograf.DashboardCell{} + for _, rsp := range respFrame { + actual = append(actual, rsp.DashboardCell) + } + + // compare actual and expected + if !cmp.Equal(actual, test.expected) { + t.Fatalf("%q - Dashboard Cells do not match: diff: %s", test.name, cmp.Diff(actual, test.expected)) + } + }) + } +} diff --git a/server/dashboards_test.go b/server/dashboards_test.go index 5c92083451..d328addad5 100644 --- a/server/dashboards_test.go +++ b/server/dashboards_test.go @@ -273,6 +273,7 @@ func Test_newDashboardResponse(t *testing.T) { "y": chronograf.Axis{ Bounds: []string{"2", "95"}, }, + "y2": chronograf.Axis{}, }, }, }, @@ -284,6 +285,11 @@ func Test_newDashboardResponse(t *testing.T) { ID: "b", W: 4, H: 4, + Axes: map[string]chronograf.Axis{ + "x": chronograf.Axis{}, + "y": chronograf.Axis{}, + "y2": chronograf.Axis{}, + }, Queries: []chronograf.DashboardQuery{ { Command: "SELECT winning_horses from grays_sports_alamanc where time > now() - 15m", From 08bf8aeff5fc83228d6aec0062398851d0a87f0e Mon Sep 17 00:00:00 2001 From: Tim Raymond Date: Thu, 27 Jul 2017 16:03:24 -0400 Subject: [PATCH 38/93] Ensure cell bounds come back as empty array The contract with the frontend states that bounds should come back as an empty array instead of null when there are no bounds present. We must explicitly specify []string{} for this to happen. --- server/cells.go | 4 +++- server/cells_test.go | 12 +++++++++--- server/dashboards_test.go | 16 ++++++++++++---- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/server/cells.go b/server/cells.go index a16cb702d5..6c7859df49 100644 --- a/server/cells.go +++ b/server/cells.go @@ -43,7 +43,9 @@ func newCellResponses(dID chronograf.DashboardID, dcells []chronograf.DashboardC for _, lbl := range labels { _, found := cell.Axes[lbl] if !found { - cell.Axes[lbl] = chronograf.Axis{} + cell.Axes[lbl] = chronograf.Axis{ + Bounds: []string{}, + } } } diff --git a/server/cells_test.go b/server/cells_test.go index 1be5981f9f..5ca5dce5e1 100644 --- a/server/cells_test.go +++ b/server/cells_test.go @@ -119,9 +119,15 @@ func Test_Service_DashboardCells(t *testing.T) { Name: "CPU", Queries: []chronograf.DashboardQuery{}, Axes: map[string]chronograf.Axis{ - "x": chronograf.Axis{}, - "y": chronograf.Axis{}, - "y2": chronograf.Axis{}, + "x": chronograf.Axis{ + Bounds: []string{}, + }, + "y": chronograf.Axis{ + Bounds: []string{}, + }, + "y2": chronograf.Axis{ + Bounds: []string{}, + }, }, }, }, diff --git a/server/dashboards_test.go b/server/dashboards_test.go index d328addad5..d659099dc9 100644 --- a/server/dashboards_test.go +++ b/server/dashboards_test.go @@ -273,7 +273,9 @@ func Test_newDashboardResponse(t *testing.T) { "y": chronograf.Axis{ Bounds: []string{"2", "95"}, }, - "y2": chronograf.Axis{}, + "y2": chronograf.Axis{ + Bounds: []string{}, + }, }, }, }, @@ -286,9 +288,15 @@ func Test_newDashboardResponse(t *testing.T) { W: 4, H: 4, Axes: map[string]chronograf.Axis{ - "x": chronograf.Axis{}, - "y": chronograf.Axis{}, - "y2": chronograf.Axis{}, + "x": chronograf.Axis{ + Bounds: []string{}, + }, + "y": chronograf.Axis{ + Bounds: []string{}, + }, + "y2": chronograf.Axis{ + Bounds: []string{}, + }, }, Queries: []chronograf.DashboardQuery{ { From 7aad7336824a495dde1c18313b0ebe576b4bc2b4 Mon Sep 17 00:00:00 2001 From: Tim Raymond Date: Fri, 28 Jul 2017 11:05:25 -0400 Subject: [PATCH 39/93] Fix data races in dashboard response construction Dashboard responses had data races because multiple goroutines were reading and modifying dashboards before sending them out on the wire. This patch introduces immutability in the construction of the response, so that each goroutine is working with its own set of dashboardResponse structs. --- server/cells.go | 27 +++++++++++++++++---------- server/dashboards.go | 31 +++++++++++++++++-------------- server/dashboards_test.go | 2 +- 3 files changed, 35 insertions(+), 25 deletions(-) diff --git a/server/cells.go b/server/cells.go index 6c7859df49..29c0bd57a2 100644 --- a/server/cells.go +++ b/server/cells.go @@ -30,27 +30,34 @@ func newCellResponses(dID chronograf.DashboardID, dcells []chronograf.DashboardC base := "/chronograf/v1/dashboards" cells := make([]dashboardCellResponse, len(dcells)) for i, cell := range dcells { - if len(cell.Queries) == 0 { - cell.Queries = make([]chronograf.DashboardQuery, 0) - } + newCell := chronograf.DashboardCell{} + + newCell.Queries = make([]chronograf.DashboardQuery, len(cell.Queries)) + copy(newCell.Queries, cell.Queries) // ensure x, y, and y2 axes always returned labels := []string{"x", "y", "y2"} - if cell.Axes == nil { - cell.Axes = make(map[string]chronograf.Axis, len(labels)) - } + newCell.Axes = make(map[string]chronograf.Axis, len(labels)) + + newCell.X = cell.X + newCell.Y = cell.Y + newCell.W = cell.W + newCell.H = cell.H + newCell.Name = cell.Name + newCell.ID = cell.ID for _, lbl := range labels { - _, found := cell.Axes[lbl] - if !found { - cell.Axes[lbl] = chronograf.Axis{ + if axis, found := cell.Axes[lbl]; !found { + newCell.Axes[lbl] = chronograf.Axis{ Bounds: []string{}, } + } else { + newCell.Axes[lbl] = axis } } cells[i] = dashboardCellResponse{ - DashboardCell: cell, + DashboardCell: newCell, Links: dashboardCellLinks{ Self: fmt.Sprintf("%s/%d/cells/%s", base, dID, cell.ID), }, diff --git a/server/dashboards.go b/server/dashboards.go index ee9bb8a602..df307a8b4f 100644 --- a/server/dashboards.go +++ b/server/dashboards.go @@ -28,20 +28,19 @@ type getDashboardsResponse struct { func newDashboardResponse(d chronograf.Dashboard) *dashboardResponse { base := "/chronograf/v1/dashboards" - DashboardDefaults(&d) - AddQueryConfigs(&d) - cells := newCellResponses(d.ID, d.Cells) - templates := newTemplateResponses(d.ID, d.Templates) + dd := AddQueryConfigs(DashboardDefaults(d)) + cells := newCellResponses(dd.ID, dd.Cells) + templates := newTemplateResponses(dd.ID, dd.Templates) return &dashboardResponse{ - ID: d.ID, - Name: d.Name, + ID: dd.ID, + Name: dd.Name, Cells: cells, Templates: templates, Links: dashboardLinks{ - Self: fmt.Sprintf("%s/%d", base, d.ID), - Cells: fmt.Sprintf("%s/%d/cells", base, d.ID), - Templates: fmt.Sprintf("%s/%d/templates", base, d.ID), + Self: fmt.Sprintf("%s/%d", base, dd.ID), + Cells: fmt.Sprintf("%s/%d/cells", base, dd.ID), + Templates: fmt.Sprintf("%s/%d/templates", base, dd.ID), }, } } @@ -229,24 +228,28 @@ func ValidDashboardRequest(d *chronograf.Dashboard) error { return err } } - DashboardDefaults(d) + (*d) = DashboardDefaults(*d) return nil } // DashboardDefaults updates the dashboard with the default values // if none are specified -func DashboardDefaults(d *chronograf.Dashboard) { +func DashboardDefaults(d chronograf.Dashboard) (newDash chronograf.Dashboard) { + newDash.Cells = make([]chronograf.DashboardCell, len(d.Cells)) for i, c := range d.Cells { CorrectWidthHeight(&c) - d.Cells[i] = c + newDash.Cells[i] = c } + return } // AddQueryConfigs updates all the celsl in the dashboard to have query config // objects corresponding to their influxql queries. -func AddQueryConfigs(d *chronograf.Dashboard) { +func AddQueryConfigs(d chronograf.Dashboard) (newDash chronograf.Dashboard) { + newDash.Cells = make([]chronograf.DashboardCell, len(d.Cells)) for i, c := range d.Cells { AddQueryConfig(&c) - d.Cells[i] = c + newDash.Cells[i] = c } + return } diff --git a/server/dashboards_test.go b/server/dashboards_test.go index d659099dc9..f7cbaa7472 100644 --- a/server/dashboards_test.go +++ b/server/dashboards_test.go @@ -128,7 +128,7 @@ func TestDashboardDefaults(t *testing.T) { }, } for _, tt := range tests { - if DashboardDefaults(&tt.d); !reflect.DeepEqual(tt.d, tt.want) { + if actual := DashboardDefaults(tt.d); !reflect.DeepEqual(actual, tt.want) { t.Errorf("%q. DashboardDefaults() = %v, want %v", tt.name, tt.d, tt.want) } } From 1a1b077a907585e9a9581a285a2f713723f3dfd6 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Mon, 31 Jul 2017 14:00:54 -0700 Subject: [PATCH 40/93] Remove axes normalizer With the `bounds` returning from the backend and the values saved as strings there's no longer a need to normalize cell state. --- .../components/CellEditorOverlay.js | 20 +------------------ 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/ui/src/dashboards/components/CellEditorOverlay.js b/ui/src/dashboards/components/CellEditorOverlay.js index 001aa5fa01..ae97b8d837 100644 --- a/ui/src/dashboards/components/CellEditorOverlay.js +++ b/ui/src/dashboards/components/CellEditorOverlay.js @@ -34,7 +34,6 @@ class CellEditorOverlay extends Component { this.handleSetActiveQueryIndex = ::this.handleSetActiveQueryIndex this.handleEditRawText = ::this.handleEditRawText this.handleSetRange = ::this.handleSetRange - this.normalizeAxes = ::this.normalizeAxes const {cell: {name, type, queries, axes}} = props @@ -82,7 +81,6 @@ class CellEditorOverlay extends Component { handleSetRange(e) { const {min, max} = e.target.form - // TODO: handle "" for min and max value this.setState({ axes: { y: { @@ -111,10 +109,10 @@ class CellEditorOverlay extends Component { queriesWorkingDraft, cellWorkingType: type, cellWorkingName: name, + axes, } = this.state const {cell} = this.props - const axes = this.normalizeAxes() const queries = queriesWorkingDraft.map(q => { const timeRange = q.range || {upper: null, lower: ':dashboardTime:'} @@ -137,22 +135,6 @@ class CellEditorOverlay extends Component { }) } - normalizeAxes() { - const axes = this.state.axes - const bounds = _.get(axes, ['y', 'bounds'], false) - if (!bounds && !bounds.length) { - return {...axes, y: {bounds: []}} - } - - const [min, max] = bounds - if (min === '' || max === '') { - // TODO: throw requirement error - return - } - - return {...axes, y: {bounds: [+min, +max]}} - } - handleSelectGraphType(graphType) { this.setState({cellWorkingType: graphType}) } From e703107644c798b453473b157572686ddf7cd5d7 Mon Sep 17 00:00:00 2001 From: Tim Raymond Date: Mon, 31 Jul 2017 17:24:43 -0400 Subject: [PATCH 41/93] Copy missing properties from Dashboards When creating new dashboards to set defaults, not all properties of the dashboard were being copied. This ensures that they are so that zero values are not used for things like the ID and Name. --- server/dashboards.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/server/dashboards.go b/server/dashboards.go index df307a8b4f..62e6aac1c5 100644 --- a/server/dashboards.go +++ b/server/dashboards.go @@ -235,7 +235,11 @@ func ValidDashboardRequest(d *chronograf.Dashboard) error { // DashboardDefaults updates the dashboard with the default values // if none are specified func DashboardDefaults(d chronograf.Dashboard) (newDash chronograf.Dashboard) { + newDash.ID = d.ID + newDash.Templates = d.Templates + newDash.Name = d.Name newDash.Cells = make([]chronograf.DashboardCell, len(d.Cells)) + for i, c := range d.Cells { CorrectWidthHeight(&c) newDash.Cells[i] = c @@ -246,7 +250,11 @@ func DashboardDefaults(d chronograf.Dashboard) (newDash chronograf.Dashboard) { // AddQueryConfigs updates all the celsl in the dashboard to have query config // objects corresponding to their influxql queries. func AddQueryConfigs(d chronograf.Dashboard) (newDash chronograf.Dashboard) { + newDash.ID = d.ID + newDash.Templates = d.Templates + newDash.Name = d.Name newDash.Cells = make([]chronograf.DashboardCell, len(d.Cells)) + for i, c := range d.Cells { AddQueryConfig(&c) newDash.Cells[i] = c From d0e322ef55be24646b29434756de0ed053e9cda1 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 1 Aug 2017 09:10:08 -0700 Subject: [PATCH 42/93] Save y range for user --- ui/src/dashboards/components/CellEditorOverlay.js | 9 ++------- ui/src/data_explorer/components/Visualization.js | 4 ++-- ui/src/shared/parsing/getRangeForDygraph.js | 8 ++++++-- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/ui/src/dashboards/components/CellEditorOverlay.js b/ui/src/dashboards/components/CellEditorOverlay.js index ae97b8d837..595642a60c 100644 --- a/ui/src/dashboards/components/CellEditorOverlay.js +++ b/ui/src/dashboards/components/CellEditorOverlay.js @@ -80,14 +80,9 @@ class CellEditorOverlay extends Component { handleSetRange(e) { const {min, max} = e.target.form + const {axes} = this.state - this.setState({ - axes: { - y: { - bounds: [min.value, max.value], - }, - }, - }) + this.setState({axes: {...axes, y: {bounds: [min.value, max.value]}}}) e.preventDefault() } diff --git a/ui/src/data_explorer/components/Visualization.js b/ui/src/data_explorer/components/Visualization.js index 4729009040..66c34664d4 100644 --- a/ui/src/data_explorer/components/Visualization.js +++ b/ui/src/data_explorer/components/Visualization.js @@ -6,7 +6,7 @@ import VisView from 'src/data_explorer/components/VisView' import {GRAPH, TABLE} from 'shared/constants' import _ from 'lodash' -const {array, arrayOf, bool, func, number, shape, string} = PropTypes +const {arrayOf, bool, func, number, shape, string} = PropTypes const META_QUERY_REGEX = /^show/i const Visualization = React.createClass({ @@ -28,7 +28,7 @@ const Visualization = React.createClass({ views: arrayOf(string).isRequired, axes: shape({ y: shape({ - bounds: array, + bounds: arrayOf(string), }), }), }, diff --git a/ui/src/shared/parsing/getRangeForDygraph.js b/ui/src/shared/parsing/getRangeForDygraph.js index 1e505718b9..43eec356df 100644 --- a/ui/src/shared/parsing/getRangeForDygraph.js +++ b/ui/src/shared/parsing/getRangeForDygraph.js @@ -19,6 +19,11 @@ const getRange = ( ) => { const {value, rangeValue, operator} = ruleValues + if (userSelectedRange.length) { + const [userMin, userMax] = userSelectedRange + return [considerZero(userMin), considerZero(userMax)] + } + const subtractPadding = val => +val - Math.abs(val * PADDING_FACTOR) const addPadding = val => +val + Math.abs(val * PADDING_FACTOR) @@ -65,10 +70,9 @@ const getRange = ( return [null, null] } - const [userMin, userMax] = userSelectedRange const [min, max] = range - return [considerZero(userMin, min), considerZero(userMax, max)] + return [min, max] } export default getRange From 21edafce95f10bb571c74326511d22f4defd1827 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 1 Aug 2017 09:42:37 -0700 Subject: [PATCH 43/93] Shorten text --- ui/src/dashboards/components/Ranger.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/dashboards/components/Ranger.js b/ui/src/dashboards/components/Ranger.js index 2db044e0cc..d39caa5b88 100644 --- a/ui/src/dashboards/components/Ranger.js +++ b/ui/src/dashboards/components/Ranger.js @@ -25,7 +25,7 @@ const Ranger = ({onSetRange, axes}) => {
    Date: Tue, 1 Aug 2017 11:02:59 -0700 Subject: [PATCH 44/93] Tweak styles --- ui/src/dashboards/components/Ranger.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/ui/src/dashboards/components/Ranger.js b/ui/src/dashboards/components/Ranger.js index d39caa5b88..7dbc79bc77 100644 --- a/ui/src/dashboards/components/Ranger.js +++ b/ui/src/dashboards/components/Ranger.js @@ -9,10 +9,8 @@ const Ranger = ({onSetRange, axes}) => {
    Y Axis Controls
    -
    - +
    + { placeholder="auto" />
    -
    - +
    + Date: Tue, 1 Aug 2017 11:31:56 -0700 Subject: [PATCH 45/93] Add UI scaffolding for more axis controls --- ui/src/dashboards/components/Ranger.js | 43 ++++++++++++++++++- .../style/components/ceo-display-options.scss | 21 +++++---- 2 files changed, 54 insertions(+), 10 deletions(-) diff --git a/ui/src/dashboards/components/Ranger.js b/ui/src/dashboards/components/Ranger.js index 7dbc79bc77..1e94ac92a8 100644 --- a/ui/src/dashboards/components/Ranger.js +++ b/ui/src/dashboards/components/Ranger.js @@ -8,7 +8,34 @@ const Ranger = ({onSetRange, axes}) => { return (
    Y Axis Controls
    - + +
    + + +
    +
    + + +
    +
    + + +
    { placeholder="auto" />
    +
    + +
      +
    • Base 10
    • +
    • Base 2
    • +
    +
    +
    + +
      +
    • Linear
    • +
    • Logarithmic
    • +
    +
    ) diff --git a/ui/src/style/components/ceo-display-options.scss b/ui/src/style/components/ceo-display-options.scss index 62a9a6eac8..635c79dde4 100644 --- a/ui/src/style/components/ceo-display-options.scss +++ b/ui/src/style/components/ceo-display-options.scss @@ -30,15 +30,6 @@ color: $g11-sidewalk; @include no-user-select(); } -.display-options--row { - display: flex; - align-items: center; - flex-wrap: nowrap; - margin-bottom: 8px; - - label { margin: 0; } - input { flex: 1 0 0; } -} .viz-type-selector { @@ -138,3 +129,15 @@ .viz-type-selector--graphic-fill.graphic-fill-b, .viz-type-selector--graphic-fill.graphic-fill-c {opacity: 0.18;} } + + + +.display-options--cell .form-group .nav.nav-tablist { + display: flex; + width: 100%; + + > li { + flex: 1 0 0; + justify-content: center; + } +} From b9bac12d88e5c91ca7f503ac5d90fec395462d04 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 1 Aug 2017 13:14:31 -0700 Subject: [PATCH 46/93] Fix test and user submitted values check --- ui/spec/shared/parsing/getRangeForDygraphSpec.js | 4 ++-- ui/src/shared/parsing/getRangeForDygraph.js | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ui/spec/shared/parsing/getRangeForDygraphSpec.js b/ui/spec/shared/parsing/getRangeForDygraphSpec.js index 2cec89bcfe..d7840444f3 100644 --- a/ui/spec/shared/parsing/getRangeForDygraphSpec.js +++ b/ui/spec/shared/parsing/getRangeForDygraphSpec.js @@ -17,10 +17,10 @@ describe.only('getRangeForDygraphSpec', () => { it('does not get range when a range is provided', () => { const timeSeries = [[date, min], [date, max], [date, mid]] - const providedRange = [0, 4] + const providedRange = ['0', '4'] const actual = getRange(timeSeries, providedRange) - expect(actual).to.deep.equal(providedRange) + expect(actual).to.deep.equal([0, 4]) }) it('gets the range for multiple timeSeries', () => { diff --git a/ui/src/shared/parsing/getRangeForDygraph.js b/ui/src/shared/parsing/getRangeForDygraph.js index 43eec356df..8d7696dbd7 100644 --- a/ui/src/shared/parsing/getRangeForDygraph.js +++ b/ui/src/shared/parsing/getRangeForDygraph.js @@ -18,9 +18,9 @@ const getRange = ( ruleValues = {value: null, rangeValue: null} ) => { const {value, rangeValue, operator} = ruleValues + const [userMin, userMax] = userSelectedRange - if (userSelectedRange.length) { - const [userMin, userMax] = userSelectedRange + if (userMin && userMax) { return [considerZero(userMin), considerZero(userMax)] } @@ -72,7 +72,7 @@ const getRange = ( const [min, max] = range - return [min, max] + return [considerZero(userMin, min), considerZero(userMax, max)] } export default getRange From 2270604c6841d7ea632fe59495f007319a930851 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 1 Aug 2017 13:24:52 -0700 Subject: [PATCH 47/93] Comment out undone features --- ui/src/dashboards/components/Ranger.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ui/src/dashboards/components/Ranger.js b/ui/src/dashboards/components/Ranger.js index 1e94ac92a8..e97fec0ca9 100644 --- a/ui/src/dashboards/components/Ranger.js +++ b/ui/src/dashboards/components/Ranger.js @@ -1,6 +1,7 @@ import React, {PropTypes} from 'react' import _ from 'lodash' +// TODO: add logic for for Prefix, Suffix, Scale, and Multiplier const Ranger = ({onSetRange, axes}) => { const min = _.get(axes, ['y', 'bounds', '0'], '') const max = _.get(axes, ['y', 'bounds', '1'], '') @@ -9,7 +10,7 @@ const Ranger = ({onSetRange, axes}) => {
    Y Axis Controls
    -
    + {/*
    { name="suffix" id="suffix" /> -
    +
    */}
    { placeholder="auto" />
    -
    + {/*
    • Base 10
    • @@ -73,7 +74,7 @@ const Ranger = ({onSetRange, axes}) => {
    • Linear
    • Logarithmic
    -
    +
    */}
    ) From b9754fd5d54830ad2555341244a34913da1c3fb5 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 1 Aug 2017 14:12:40 -0700 Subject: [PATCH 48/93] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7613754c15..f8780fc094 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ ### Features 1. [#1717](https://github.com/influxdata/chronograf/pull/1717): View server generated TICKscripts 1. [#1681](https://github.com/influxdata/chronograf/pull/1681): Add the ability to select Custom Time Ranges in the Hostpages, Data Explorer, and Dashboards. +1. [#1714](https://github.com/influxdata/chronograf/pull/1714): Add ability for users to set y-axis bounds ### UI Improvements From eb6f437c43bb5402600635243bda30ec2fbe5493 Mon Sep 17 00:00:00 2001 From: Alex P Date: Tue, 1 Aug 2017 14:38:12 -0700 Subject: [PATCH 49/93] Fine tune UI for axis controls --- ui/src/dashboards/components/Ranger.js | 47 +++++++++++++------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/ui/src/dashboards/components/Ranger.js b/ui/src/dashboards/components/Ranger.js index 1e94ac92a8..9f3bdb83ec 100644 --- a/ui/src/dashboards/components/Ranger.js +++ b/ui/src/dashboards/components/Ranger.js @@ -9,17 +9,8 @@ const Ranger = ({onSetRange, axes}) => {
    Y Axis Controls
    -
    - - -
    -
    - +
    + { id="label" />
    -
    - - -
    { />
    - + + +
    +
    + + +
    +
    +
      -
    • Base 10
    • -
    • Base 2
    • +
    • K/M/B
    • +
    • K/M/G
    @@ -74,6 +74,7 @@ const Ranger = ({onSetRange, axes}) => {
  • Logarithmic
+
) From 6f4c386e69215047b3785a2a634fac6e93aa5933 Mon Sep 17 00:00:00 2001 From: Jared Scheib Date: Tue, 1 Aug 2017 16:10:14 -0700 Subject: [PATCH 50/93] Improve error message on JSON Feed GET fail --- ui/src/status/actions/index.js | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/ui/src/status/actions/index.js b/ui/src/status/actions/index.js index d985035b30..c64594a678 100644 --- a/ui/src/status/actions/index.js +++ b/ui/src/status/actions/index.js @@ -7,8 +7,6 @@ import {errorThrown} from 'shared/actions/errors' import * as actionTypes from 'src/status/constants/actionTypes' -import {HTTP_NOT_FOUND} from 'shared/constants' - const fetchJSONFeedRequested = () => ({ type: actionTypes.FETCH_JSON_FEED_REQUESTED, }) @@ -45,15 +43,11 @@ export const fetchJSONFeedAsync = url => async dispatch => { } catch (error) { console.error(error) dispatch(fetchJSONFeedFailed()) - if (error.status === HTTP_NOT_FOUND) { - dispatch( - errorThrown( - error, - `Failed to fetch News Feed. JSON Feed at '${url}' returned 404 (Not Found)` - ) + dispatch( + errorThrown( + error, + `Failed to fetch JSON Feed for News Feed from '${url}'` ) - } else { - dispatch(errorThrown(error, 'Failed to fetch NewsFeed')) - } + ) } } From c0f4f78a6d6bbae6ba7c15d9a66e4737ce567549 Mon Sep 17 00:00:00 2001 From: Jared Scheib Date: Tue, 1 Aug 2017 16:16:04 -0700 Subject: [PATCH 51/93] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d10faa318d..ac734b2624 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ ### UI Improvements 1. [#1796](https://github.com/influxdata/chronograf/pull/1796): Add spinner to indicate data is being written 1. [#1800](https://github.com/influxdata/chronograf/pull/1796): Embiggen text area for line protocol manual entry in Data Explorer's Write Data overlay +1. [#1812](https://github.com/influxdata/chronograf/pull/1812): Improve error message when request for Status Page News Feed fails ## v1.3.5.0 [2017-07-27] ### Bug Fixes From ce02479704b77ee9a9aea053d8b293f3191fbb92 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 1 Aug 2017 16:16:24 -0700 Subject: [PATCH 52/93] Make linter happy --- ui/src/dashboards/components/Ranger.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ui/src/dashboards/components/Ranger.js b/ui/src/dashboards/components/Ranger.js index 33f8f27a82..09ce134005 100644 --- a/ui/src/dashboards/components/Ranger.js +++ b/ui/src/dashboards/components/Ranger.js @@ -10,7 +10,7 @@ const Ranger = ({onSetRange, axes}) => {
Y Axis Controls
- {/*
+ {/*
{ name="label" id="label" /> -
*/} +
*/}
{ placeholder="auto" />
- {/*
+ {/*
{
  • Linear
  • Logarithmic
  • -
    */} - +
    */}
    ) From a9789afe847290daaac7e472e9d21bb3229ad4de Mon Sep 17 00:00:00 2001 From: Jared Scheib Date: Tue, 1 Aug 2017 16:17:11 -0700 Subject: [PATCH 53/93] Fix link in changelog update for #1800 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d10faa318d..48c7e6109b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ ### Features ### UI Improvements 1. [#1796](https://github.com/influxdata/chronograf/pull/1796): Add spinner to indicate data is being written -1. [#1800](https://github.com/influxdata/chronograf/pull/1796): Embiggen text area for line protocol manual entry in Data Explorer's Write Data overlay +1. [#1800](https://github.com/influxdata/chronograf/pull/1800): Embiggen text area for line protocol manual entry in Data Explorer's Write Data overlay ## v1.3.5.0 [2017-07-27] ### Bug Fixes From 3897228e30976ff055615e6cb75d0263616fa559 Mon Sep 17 00:00:00 2001 From: Alex P Date: Tue, 1 Aug 2017 19:46:11 -0700 Subject: [PATCH 54/93] WIP "one or any" component --- ui/src/dashboards/components/Ranger.js | 29 ++++--- ui/src/style/chronograf.scss | 1 + ui/src/style/components/one-or-any.scss | 104 ++++++++++++++++++++++++ 3 files changed, 123 insertions(+), 11 deletions(-) create mode 100644 ui/src/style/components/one-or-any.scss diff --git a/ui/src/dashboards/components/Ranger.js b/ui/src/dashboards/components/Ranger.js index 09ce134005..70902d5f45 100644 --- a/ui/src/dashboards/components/Ranger.js +++ b/ui/src/dashboards/components/Ranger.js @@ -20,19 +20,26 @@ const Ranger = ({onSetRange, axes}) => { />
    */}
    - - + +
    +
    auto
    +
    +
    +
    + +
    - + div { + margin: 0 10px; + z-index: 3; + width: 28px; + height: 8px; + border-radius: 4px; + background-color: $g6-smoke; + position: relative; + + // Dot + &:after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + width: 14px; + height: 14px; + border-radius: 50%; + background-color: $c-pool; + transition: + background-color 0.25s ease, + transform 0.25s ease; + transform: translate(-100%,-50%); + } + } + + // Background Gradients + &:before, &:after { + content: ''; + display: block; + position: absolute; + width: 100%; + height: 100%; + transition: opacity 0.25s ease; + } + // Left + &:before { + z-index: 2; + @include gradient-h($g2-kevlar,$g3-castle); + opacity: 1; + } + // Right + &:after { + @include gradient-h($g3-castle,$g2-kevlar); + z-index: 1; + } + + &:hover { + cursor: pointer; + > div:after {background-color: $c-laser;} + } +} +// Customize form input +.one-or-any > input.form-control { + border-radius: 0 4px 4px 0; + font-family: $code-font; +} +// Toggled state +.one-or-any.toggled { + .one-or-any--switch > div:after {transform: translate(0%,-50%);} + // Fade out left, fade in right + .one-or-any--switch:before {opacity: 0;} + // Make auto look disabled + .one-or-any--auto { + background-color: $g3-castle; + color: $g8-storm; + font-style: italic; + } +} From b7bb9cb33905afabfcd45baa38aa924015b272db Mon Sep 17 00:00:00 2001 From: Alex P Date: Tue, 1 Aug 2017 19:51:20 -0700 Subject: [PATCH 55/93] Rename classes and element --- ui/src/dashboards/components/Ranger.js | 4 +- ui/src/style/components/one-or-any.scss | 60 ++++++++++++------------- 2 files changed, 31 insertions(+), 33 deletions(-) diff --git a/ui/src/dashboards/components/Ranger.js b/ui/src/dashboards/components/Ranger.js index 70902d5f45..b755494ee6 100644 --- a/ui/src/dashboards/components/Ranger.js +++ b/ui/src/dashboards/components/Ranger.js @@ -23,8 +23,8 @@ const Ranger = ({onSetRange, axes}) => {
    auto
    -
    -
    +
    +
    div { - margin: 0 10px; - z-index: 3; - width: 28px; - height: 8px; - border-radius: 4px; - background-color: $g6-smoke; - position: relative; - - // Dot - &:after { - content: ''; - position: absolute; - top: 50%; - left: 50%; - width: 14px; - height: 14px; - border-radius: 50%; - background-color: $c-pool; - transition: - background-color 0.25s ease, - transform 0.25s ease; - transform: translate(-100%,-50%); - } - } - // Background Gradients &:before, &:after { content: ''; @@ -82,7 +55,32 @@ &:hover { cursor: pointer; - > div:after {background-color: $c-laser;} + .one-or-any--groove-knob:after {background-color: $c-laser;} + } +} +.one-or-any--groove-knob { + margin: 0 10px; + z-index: 3; + width: 28px; + height: 8px; + border-radius: 4px; + background-color: $g6-smoke; + position: relative; + + // Knob + &:after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + width: 14px; + height: 14px; + border-radius: 50%; + background-color: $c-pool; + transition: + background-color 0.25s ease, + transform 0.25s ease; + transform: translate(-100%,-50%); } } // Customize form input @@ -92,9 +90,9 @@ } // Toggled state .one-or-any.toggled { - .one-or-any--switch > div:after {transform: translate(0%,-50%);} + .one-or-any--groove-knob:after {transform: translate(0%,-50%);} // Fade out left, fade in right - .one-or-any--switch:before {opacity: 0;} + .one-or-any--toggle:before {opacity: 0;} // Make auto look disabled .one-or-any--auto { background-color: $g3-castle; From 69dbd0e988a8350ad32731a35fb7353390cf30d2 Mon Sep 17 00:00:00 2001 From: Alex Paxton Date: Tue, 1 Aug 2017 20:42:29 -0700 Subject: [PATCH 56/93] Wire up GrooveKnob to be fully functional Signed-off-by: Jared Scheib --- ui/src/shared/components/GrooveKnob.js | 83 +++++++++++++++++++++++++ ui/src/style/components/one-or-any.scss | 8 +-- 2 files changed, 87 insertions(+), 4 deletions(-) create mode 100644 ui/src/shared/components/GrooveKnob.js diff --git a/ui/src/shared/components/GrooveKnob.js b/ui/src/shared/components/GrooveKnob.js new file mode 100644 index 0000000000..134e2d71a7 --- /dev/null +++ b/ui/src/shared/components/GrooveKnob.js @@ -0,0 +1,83 @@ +import React, {Component, PropTypes} from 'react' +import classnames from 'classnames' + +class GrooveKnob extends Component { + constructor(props) { + super(props) + + const {leftValue, rightValue} = props + + this.state = { + leftValue, + rightValue, + } + + this.handleChangeRightValue = ::this.handleChangeRightValue + this.handleToggleLeftValue = ::this.handleToggleLeftValue + this.handleSetValues = ::this.handleSetValues + } + + handleChangeRightValue(newValue) { + this.setState({rightValue: newValue}, this.handleSetValues) + } + + handleToggleLeftValue() { + const {leftValue} = this.state + + this.setState({leftValue: !leftValue}, this.handleSetValues) + } + + handleSetValues() { + const {onSetValues} = this.props + const {leftValue, rightValue} = this.state + + onSetValues({leftValue, rightValue}) + } + + render() { + const {leftLabel, rightLabel} = this.props + const {leftValue: useLeftValue, rightValue} = this.state + + return ( +
    +
    {leftLabel}
    +
    +
    +
    + this.handleChangeRightValue(e.target.value)} + placeholder={rightLabel} + disabled={useLeftValue} + /> +
    + ) + } +} + +GrooveKnob.defaultProps = { + leftLabel: 'auto', + leftValue: true, + rightLabel: 'Custom Value', + rightValue: null, +} +const {bool, func, number, string} = PropTypes + +GrooveKnob.propTypes = { + leftLabel: string, + leftValue: bool, + rightLabel: string, + rightValue: number, + onSetValues: func.isRequired, +} + +export default GrooveKnob diff --git a/ui/src/style/components/one-or-any.scss b/ui/src/style/components/one-or-any.scss index 7b23d3bc7a..6580d5cbd1 100644 --- a/ui/src/style/components/one-or-any.scss +++ b/ui/src/style/components/one-or-any.scss @@ -24,7 +24,7 @@ background-color 0.25s ease, color 0.25s ease; } -.one-or-any--toggle { +.one-or-any--switch { display: flex; align-items: center; border: 2px solid $g5-pepper; @@ -88,11 +88,11 @@ border-radius: 0 4px 4px 0; font-family: $code-font; } -// Toggled state -.one-or-any.toggled { +// When using right value +.one-or-any.use-right-value { .one-or-any--groove-knob:after {transform: translate(0%,-50%);} // Fade out left, fade in right - .one-or-any--toggle:before {opacity: 0;} + .one-or-any--switch:before {opacity: 0;} // Make auto look disabled .one-or-any--auto { background-color: $g3-castle; From c0cc7308576968f5c72ca30f8b02b8df888d3d7b Mon Sep 17 00:00:00 2001 From: Alex Paxton Date: Tue, 1 Aug 2017 20:43:45 -0700 Subject: [PATCH 57/93] WIP Pass values from GrooveKnob to parent Signed-off-by: Jared Scheib --- ui/src/dashboards/components/Ranger.js | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/ui/src/dashboards/components/Ranger.js b/ui/src/dashboards/components/Ranger.js index b755494ee6..70cd9aeb9d 100644 --- a/ui/src/dashboards/components/Ranger.js +++ b/ui/src/dashboards/components/Ranger.js @@ -1,6 +1,8 @@ import React, {PropTypes} from 'react' import _ from 'lodash' +import GrooveKnob from 'shared/components/GrooveKnob' + // TODO: add logic for for Prefix, Suffix, Scale, and Multiplier const Ranger = ({onSetRange, axes}) => { const min = _.get(axes, ['y', 'bounds', '0'], '') @@ -21,22 +23,13 @@ const Ranger = ({onSetRange, axes}) => {
    */}
    -
    -
    auto
    -
    -
    -
    - -
    + { + console.log(leftValue, rightValue) + // onSetRange() + }} + rightValue={Number(min)} + />
    From 803906e25a1c750e7f7dd4015b05ed48fbef3cfa Mon Sep 17 00:00:00 2001 From: Tim Raymond Date: Wed, 2 Aug 2017 10:58:01 -0400 Subject: [PATCH 58/93] Return nothing from previously set bounds Previously, if bounds were not set, the default value that would be returned was ["0", "0"], which is incorrect. This now returns [] when there was nothing set for a particular axis. --- bolt/internal/internal.go | 18 ++++---- bolt/internal/internal_test.go | 75 +++++++++++++++++++++++++++++++++- 2 files changed, 82 insertions(+), 11 deletions(-) diff --git a/bolt/internal/internal.go b/bolt/internal/internal.go index cca08cdd10..4b64a03625 100644 --- a/bolt/internal/internal.go +++ b/bolt/internal/internal.go @@ -2,7 +2,6 @@ package internal import ( "encoding/json" - "strconv" "github.com/gogo/protobuf/proto" "github.com/influxdata/chronograf" @@ -268,16 +267,15 @@ func UnmarshalDashboard(data []byte, d *chronograf.Dashboard) error { axes := make(map[string]chronograf.Axis, len(c.Axes)) for a, r := range c.Axes { - axis := chronograf.Axis{} - // repair legacy bounds - for _, bound := range r.LegacyBounds { - axis.Bounds = append(axis.Bounds, strconv.FormatInt(bound, 10)) + if r.Bounds != nil { + axes[a] = chronograf.Axis{ + Bounds: r.Bounds, + } + } else { + axes[a] = chronograf.Axis{ + Bounds: []string{}, + } } - - if len(r.Bounds) > 0 { - axis.Bounds = r.Bounds - } - axes[a] = axis } cells[i] = chronograf.DashboardCell{ diff --git a/bolt/internal/internal_test.go b/bolt/internal/internal_test.go index 7eb86ac308..82abcbad2d 100644 --- a/bolt/internal/internal_test.go +++ b/bolt/internal/internal_test.go @@ -201,7 +201,80 @@ func Test_MarshalDashboard_WithLegacyBounds(t *testing.T) { }, Axes: map[string]chronograf.Axis{ "y": chronograf.Axis{ - Bounds: []string{"0", "5"}, + Bounds: []string{}, + }, + }, + Type: "line", + }, + }, + Templates: []chronograf.Template{}, + Name: "Dashboard", + } + + var actual chronograf.Dashboard + if buf, err := internal.MarshalDashboard(dashboard); err != nil { + t.Fatal("Error marshaling dashboard: err", err) + } else if err := internal.UnmarshalDashboard(buf, &actual); err != nil { + t.Fatal("Error unmarshaling dashboard: err:", err) + } else if !cmp.Equal(expected, actual) { + t.Fatalf("Dashboard protobuf copy error: diff follows:\n%s", cmp.Diff(expected, actual)) + } +} + +func Test_MarshalDashboard_WithNoLegacyBounds(t *testing.T) { + dashboard := chronograf.Dashboard{ + ID: 1, + Cells: []chronograf.DashboardCell{ + { + ID: "9b5367de-c552-4322-a9e8-7f384cbd235c", + X: 0, + Y: 0, + W: 4, + H: 4, + Name: "Super awesome query", + Queries: []chronograf.DashboardQuery{ + { + Command: "select * from cpu", + Label: "CPU Utilization", + Range: &chronograf.Range{ + Upper: int64(100), + }, + }, + }, + Axes: map[string]chronograf.Axis{ + "y": chronograf.Axis{ + LegacyBounds: [2]int64{}, + }, + }, + Type: "line", + }, + }, + Templates: []chronograf.Template{}, + Name: "Dashboard", + } + + expected := chronograf.Dashboard{ + ID: 1, + Cells: []chronograf.DashboardCell{ + { + ID: "9b5367de-c552-4322-a9e8-7f384cbd235c", + X: 0, + Y: 0, + W: 4, + H: 4, + Name: "Super awesome query", + Queries: []chronograf.DashboardQuery{ + { + Command: "select * from cpu", + Label: "CPU Utilization", + Range: &chronograf.Range{ + Upper: int64(100), + }, + }, + }, + Axes: map[string]chronograf.Axis{ + "y": chronograf.Axis{ + Bounds: []string{}, }, }, Type: "line", From e545f91d8f59e44e6c31efae83c5bd9ace7935ea Mon Sep 17 00:00:00 2001 From: Tim Raymond Date: Wed, 2 Aug 2017 11:12:47 -0400 Subject: [PATCH 59/93] Add Label to DashboardCell Axis It's useful for the frontend to be able to specify a label on a particular axis. This adds a property to Axis to facilitate that. --- bolt/internal/internal.go | 2 + bolt/internal/internal.pb.go | 123 +++++++++++++++++---------------- bolt/internal/internal.proto | 3 +- bolt/internal/internal_test.go | 1 + chronograf.go | 5 +- server/dashboards_test.go | 2 + 6 files changed, 72 insertions(+), 64 deletions(-) diff --git a/bolt/internal/internal.go b/bolt/internal/internal.go index 4b64a03625..9869f8f1f3 100644 --- a/bolt/internal/internal.go +++ b/bolt/internal/internal.go @@ -189,6 +189,7 @@ func MarshalDashboard(d chronograf.Dashboard) ([]byte, error) { axes[a] = &Axis{ Bounds: r.Bounds, LegacyBounds: axis[:], + Label: r.Label, } } @@ -270,6 +271,7 @@ func UnmarshalDashboard(data []byte, d *chronograf.Dashboard) error { if r.Bounds != nil { axes[a] = chronograf.Axis{ Bounds: r.Bounds, + Label: r.Label, } } else { axes[a] = chronograf.Axis{ diff --git a/bolt/internal/internal.pb.go b/bolt/internal/internal.pb.go index c81dd610fc..59de1a3216 100644 --- a/bolt/internal/internal.pb.go +++ b/bolt/internal/internal.pb.go @@ -119,6 +119,7 @@ func (m *DashboardCell) GetAxes() map[string]*Axis { type Axis struct { LegacyBounds []int64 `protobuf:"varint,1,rep,name=legacyBounds" json:"legacyBounds,omitempty"` Bounds []string `protobuf:"bytes,2,rep,name=bounds" json:"bounds,omitempty"` + Label string `protobuf:"bytes,3,opt,name=label,proto3" json:"label,omitempty"` } func (m *Axis) Reset() { *m = Axis{} } @@ -313,65 +314,65 @@ func init() { func init() { proto.RegisterFile("internal.proto", fileDescriptorInternal) } var fileDescriptorInternal = []byte{ - // 950 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xbc, 0x56, 0xcd, 0x8e, 0xe3, 0x44, - 0x10, 0x56, 0xc7, 0x76, 0x12, 0xd7, 0xcc, 0x0e, 0xa8, 0xb5, 0x62, 0xcd, 0x72, 0x09, 0x16, 0x48, - 0x01, 0x89, 0x01, 0xed, 0x0a, 0x09, 0x71, 0x4b, 0x26, 0x68, 0x15, 0x66, 0x76, 0x19, 0x3a, 0x33, - 0xc3, 0x09, 0xad, 0x3a, 0x49, 0x25, 0x63, 0xad, 0x13, 0x9b, 0xb6, 0x3d, 0x89, 0x5f, 0x81, 0x13, - 0x4f, 0x80, 0x84, 0xc4, 0x89, 0x23, 0x2f, 0xc0, 0x43, 0xf0, 0x42, 0xa8, 0xba, 0xdb, 0x3f, 0x61, - 0x67, 0xd1, 0x9e, 0xb8, 0xf5, 0x57, 0xd5, 0xf9, 0xda, 0xf5, 0xd5, 0x57, 0xa5, 0xc0, 0x49, 0xb4, - 0xcd, 0x51, 0x6d, 0x65, 0x7c, 0x9a, 0xaa, 0x24, 0x4f, 0x78, 0xbf, 0xc2, 0xe1, 0xcf, 0x1d, 0xe8, - 0xce, 0x92, 0x42, 0x2d, 0x90, 0x9f, 0x40, 0x67, 0x3a, 0x09, 0xd8, 0x80, 0x0d, 0x1d, 0xd1, 0x99, - 0x4e, 0x38, 0x07, 0xf7, 0x85, 0xdc, 0x60, 0xd0, 0x19, 0xb0, 0xa1, 0x2f, 0xf4, 0x99, 0x62, 0x57, - 0x65, 0x8a, 0x81, 0x63, 0x62, 0x74, 0xe6, 0x8f, 0xa1, 0x7f, 0x9d, 0x11, 0xdb, 0x06, 0x03, 0x57, - 0xc7, 0x6b, 0x4c, 0xb9, 0x4b, 0x99, 0x65, 0xbb, 0x44, 0x2d, 0x03, 0xcf, 0xe4, 0x2a, 0xcc, 0xdf, - 0x05, 0xe7, 0x5a, 0x5c, 0x04, 0x5d, 0x1d, 0xa6, 0x23, 0x0f, 0xa0, 0x37, 0xc1, 0x95, 0x2c, 0xe2, - 0x3c, 0xe8, 0x0d, 0xd8, 0xb0, 0x2f, 0x2a, 0x48, 0x3c, 0x57, 0x18, 0xe3, 0x5a, 0xc9, 0x55, 0xd0, - 0x37, 0x3c, 0x15, 0xe6, 0xa7, 0xc0, 0xa7, 0xdb, 0x0c, 0x17, 0x85, 0xc2, 0xd9, 0xab, 0x28, 0xbd, - 0x41, 0x15, 0xad, 0xca, 0xc0, 0xd7, 0x04, 0xf7, 0x64, 0xe8, 0x95, 0xe7, 0x98, 0x4b, 0x7a, 0x1b, - 0x34, 0x55, 0x05, 0xc3, 0x5f, 0x18, 0xf8, 0x13, 0x99, 0xdd, 0xce, 0x13, 0xa9, 0x96, 0x6f, 0xa5, - 0xc7, 0x67, 0xe0, 0x2d, 0x30, 0x8e, 0xb3, 0xc0, 0x19, 0x38, 0xc3, 0xa3, 0x27, 0x8f, 0x4e, 0x6b, - 0xa1, 0x6b, 0x9e, 0x33, 0x8c, 0x63, 0x61, 0x6e, 0xf1, 0x2f, 0xc0, 0xcf, 0x71, 0x93, 0xc6, 0x32, - 0xc7, 0x2c, 0x70, 0xf5, 0x4f, 0x78, 0xf3, 0x93, 0x2b, 0x9b, 0x12, 0xcd, 0xa5, 0xf0, 0x8f, 0x0e, - 0x3c, 0x38, 0xa0, 0xe2, 0xc7, 0xc0, 0xf6, 0xfa, 0xab, 0x3c, 0xc1, 0xf6, 0x84, 0x4a, 0xfd, 0x45, - 0x9e, 0x60, 0x25, 0xa1, 0x9d, 0xee, 0x8d, 0x27, 0xd8, 0x8e, 0xd0, 0xad, 0xee, 0x88, 0x27, 0xd8, - 0x2d, 0xff, 0x04, 0x7a, 0x3f, 0x15, 0xa8, 0x22, 0xcc, 0x02, 0x4f, 0xbf, 0xfc, 0x4e, 0xf3, 0xf2, - 0xf7, 0x05, 0xaa, 0x52, 0x54, 0x79, 0xaa, 0x54, 0x77, 0xd3, 0xb4, 0x46, 0x9f, 0x29, 0x96, 0x53, - 0xe7, 0x7b, 0x26, 0x46, 0x67, 0xab, 0x90, 0xe9, 0x07, 0x29, 0xf4, 0x25, 0xb8, 0x72, 0x8f, 0x59, - 0xe0, 0x6b, 0xfe, 0x0f, 0xdf, 0x20, 0xc6, 0xe9, 0x68, 0x8f, 0xd9, 0x37, 0xdb, 0x5c, 0x95, 0x42, - 0x5f, 0x7f, 0xfc, 0x0c, 0xfc, 0x3a, 0x44, 0xae, 0x78, 0x85, 0xa5, 0x2e, 0xd0, 0x17, 0x74, 0xe4, - 0x1f, 0x81, 0x77, 0x27, 0xe3, 0xc2, 0x08, 0x7f, 0xf4, 0xe4, 0xa4, 0xa1, 0x1d, 0xed, 0xa3, 0x4c, - 0x98, 0xe4, 0xd7, 0x9d, 0xaf, 0x58, 0x38, 0x06, 0x97, 0x42, 0x3c, 0x84, 0xe3, 0x18, 0xd7, 0x72, - 0x51, 0x8e, 0x93, 0x62, 0xbb, 0xcc, 0x02, 0x36, 0x70, 0x86, 0x8e, 0x38, 0x88, 0xf1, 0xf7, 0xa0, - 0x3b, 0x37, 0xd9, 0xce, 0xc0, 0x19, 0xfa, 0xc2, 0xa2, 0xf0, 0x2f, 0x46, 0x56, 0x33, 0xf2, 0xb7, - 0x2c, 0x60, 0x0a, 0x7c, 0x1f, 0xfa, 0xd4, 0x9a, 0x97, 0x77, 0x52, 0x59, 0x1b, 0xf4, 0x08, 0xdf, - 0x48, 0xc5, 0x3f, 0x87, 0xae, 0xfe, 0x90, 0x7b, 0xac, 0x50, 0xd1, 0xdd, 0x50, 0x5e, 0xd8, 0x6b, - 0xb5, 0xa0, 0x6e, 0x4b, 0xd0, 0x87, 0xe0, 0xc5, 0x72, 0x8e, 0xb1, 0x9d, 0x15, 0x03, 0xc8, 0x64, - 0xd4, 0x99, 0x52, 0xf7, 0xe3, 0x5e, 0x66, 0xd3, 0x3f, 0x73, 0x2b, 0xbc, 0x86, 0x07, 0x07, 0x2f, - 0xd6, 0x2f, 0xb1, 0xc3, 0x97, 0x1a, 0x51, 0x7d, 0x2b, 0x22, 0x8d, 0x59, 0x86, 0x31, 0x2e, 0x72, - 0x5c, 0x6a, 0x1b, 0xf5, 0x45, 0x8d, 0xc3, 0xdf, 0x58, 0xc3, 0xab, 0xdf, 0xa3, 0x41, 0x5a, 0x24, - 0x9b, 0x8d, 0xdc, 0x2e, 0x2d, 0x75, 0x05, 0x49, 0xb7, 0xe5, 0xdc, 0x52, 0x77, 0x96, 0x73, 0xc2, - 0x2a, 0xb5, 0x4b, 0xa3, 0xa3, 0x52, 0x3e, 0x80, 0xa3, 0x0d, 0xca, 0xac, 0x50, 0xb8, 0xc1, 0x6d, - 0x6e, 0x25, 0x68, 0x87, 0xf8, 0x23, 0xe8, 0xe5, 0x72, 0xfd, 0x92, 0xac, 0x60, 0xb4, 0xe8, 0xe6, - 0x72, 0x7d, 0x8e, 0x25, 0xff, 0x00, 0xfc, 0x55, 0x84, 0xf1, 0x52, 0xa7, 0x8c, 0x41, 0xfb, 0x3a, - 0x70, 0x8e, 0x65, 0xf8, 0x3b, 0x83, 0xee, 0x0c, 0xd5, 0x1d, 0xaa, 0xb7, 0x9a, 0xde, 0xf6, 0xe6, - 0x72, 0xfe, 0x63, 0x73, 0xb9, 0xf7, 0x6f, 0x2e, 0xaf, 0xd9, 0x5c, 0x0f, 0xc1, 0x9b, 0xa9, 0xc5, - 0x74, 0xa2, 0xbf, 0xc8, 0x11, 0x06, 0x90, 0xc7, 0x46, 0x8b, 0x3c, 0xba, 0x43, 0xbb, 0xce, 0x2c, - 0x0a, 0x7f, 0x65, 0xd0, 0xbd, 0x90, 0x65, 0x52, 0xe4, 0xaf, 0x39, 0x6c, 0x00, 0x47, 0xa3, 0x34, - 0x8d, 0xa3, 0x85, 0xcc, 0xa3, 0x64, 0x6b, 0xbf, 0xb6, 0x1d, 0xa2, 0x1b, 0xcf, 0x5b, 0xda, 0x99, - 0xef, 0x6e, 0x87, 0x68, 0x60, 0xce, 0xf4, 0x52, 0x32, 0x1b, 0xa6, 0x35, 0x30, 0x66, 0x17, 0xe9, - 0x24, 0x15, 0x38, 0x2a, 0xf2, 0x64, 0x15, 0x27, 0x3b, 0x5d, 0x49, 0x5f, 0xd4, 0x38, 0xfc, 0x9b, - 0x81, 0xfb, 0x7f, 0x2d, 0x9b, 0x63, 0x60, 0x91, 0x6d, 0x24, 0x8b, 0xea, 0xd5, 0xd3, 0x6b, 0xad, - 0x9e, 0x00, 0x7a, 0xa5, 0x92, 0xdb, 0x35, 0x66, 0x41, 0x5f, 0x4f, 0x72, 0x05, 0x75, 0x46, 0xcf, - 0x88, 0xd9, 0x39, 0xbe, 0xa8, 0x60, 0xed, 0x79, 0x68, 0x3c, 0x1f, 0xfe, 0xc9, 0xc0, 0xab, 0x9d, - 0x7b, 0x76, 0xe8, 0xdc, 0xb3, 0xc6, 0xb9, 0x93, 0x71, 0xe5, 0xdc, 0xc9, 0x98, 0xb0, 0xb8, 0xac, - 0x9c, 0x2b, 0x2e, 0x49, 0xb5, 0x67, 0x2a, 0x29, 0xd2, 0x71, 0x69, 0xe4, 0xf5, 0x45, 0x8d, 0xa9, - 0xdd, 0x3f, 0xdc, 0xa2, 0xb2, 0x35, 0xfb, 0xc2, 0x22, 0x32, 0xc7, 0x85, 0x9e, 0x6a, 0x53, 0xa5, - 0x01, 0xfc, 0x63, 0xf0, 0x04, 0x55, 0xa1, 0x4b, 0x3d, 0x10, 0x48, 0x87, 0x85, 0xc9, 0x86, 0x4f, - 0xed, 0x35, 0x62, 0xb9, 0x4e, 0x53, 0x54, 0xd6, 0xd3, 0x06, 0x68, 0xee, 0x64, 0x87, 0x66, 0x1d, - 0x39, 0xc2, 0x80, 0xf0, 0x47, 0xf0, 0x47, 0x31, 0xaa, 0x5c, 0x14, 0xf1, 0xeb, 0x4b, 0x8c, 0x83, - 0xfb, 0xed, 0xec, 0xbb, 0x17, 0xd5, 0x24, 0xd0, 0xb9, 0xf1, 0xaf, 0xf3, 0x2f, 0xff, 0x9e, 0xcb, - 0x54, 0x4e, 0x27, 0xba, 0xb1, 0x8e, 0xb0, 0x28, 0xfc, 0x14, 0x5c, 0x9a, 0x93, 0x16, 0xb3, 0xfb, - 0xa6, 0x19, 0x9b, 0x77, 0xf5, 0x3f, 0x8e, 0xa7, 0xff, 0x04, 0x00, 0x00, 0xff, 0xff, 0x33, 0xaf, - 0xb8, 0xb8, 0x83, 0x08, 0x00, 0x00, + // 957 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xbc, 0x56, 0xdd, 0x6e, 0xe3, 0x44, + 0x14, 0xd6, 0xc4, 0x76, 0x12, 0x9f, 0x76, 0x0b, 0x1a, 0xad, 0x58, 0xb3, 0xdc, 0x04, 0x0b, 0xa4, + 0x80, 0x44, 0x41, 0xbb, 0x42, 0x42, 0xdc, 0xa5, 0x0d, 0x5a, 0x85, 0x76, 0x97, 0x32, 0x69, 0x0b, + 0x37, 0x68, 0x35, 0x49, 0x4e, 0x52, 0x6b, 0x9d, 0xd8, 0x8c, 0xed, 0x26, 0x7e, 0x05, 0xae, 0x78, + 0x02, 0x24, 0x24, 0xae, 0xb8, 0xe4, 0x05, 0x78, 0x08, 0x5e, 0x08, 0x9d, 0x99, 0xf1, 0x4f, 0xd8, + 0x2e, 0xea, 0xd5, 0xde, 0xcd, 0x77, 0xce, 0xf8, 0x9b, 0x99, 0xef, 0x7c, 0xe7, 0xc8, 0x70, 0x14, + 0x6d, 0x72, 0x54, 0x1b, 0x19, 0x1f, 0xa7, 0x2a, 0xc9, 0x13, 0xde, 0xaf, 0x70, 0xf8, 0x4b, 0x07, + 0xba, 0xd3, 0xa4, 0x50, 0x73, 0xe4, 0x47, 0xd0, 0x99, 0x8c, 0x03, 0x36, 0x60, 0x43, 0x47, 0x74, + 0x26, 0x63, 0xce, 0xc1, 0x7d, 0x21, 0xd7, 0x18, 0x74, 0x06, 0x6c, 0xe8, 0x0b, 0xbd, 0xa6, 0xd8, + 0x65, 0x99, 0x62, 0xe0, 0x98, 0x18, 0xad, 0xf9, 0x63, 0xe8, 0x5f, 0x65, 0xc4, 0xb6, 0xc6, 0xc0, + 0xd5, 0xf1, 0x1a, 0x53, 0xee, 0x42, 0x66, 0xd9, 0x36, 0x51, 0x8b, 0xc0, 0x33, 0xb9, 0x0a, 0xf3, + 0x77, 0xc1, 0xb9, 0x12, 0xe7, 0x41, 0x57, 0x87, 0x69, 0xc9, 0x03, 0xe8, 0x8d, 0x71, 0x29, 0x8b, + 0x38, 0x0f, 0x7a, 0x03, 0x36, 0xec, 0x8b, 0x0a, 0x12, 0xcf, 0x25, 0xc6, 0xb8, 0x52, 0x72, 0x19, + 0xf4, 0x0d, 0x4f, 0x85, 0xf9, 0x31, 0xf0, 0xc9, 0x26, 0xc3, 0x79, 0xa1, 0x70, 0xfa, 0x2a, 0x4a, + 0xaf, 0x51, 0x45, 0xcb, 0x32, 0xf0, 0x35, 0xc1, 0x1d, 0x19, 0x3a, 0xe5, 0x39, 0xe6, 0x92, 0xce, + 0x06, 0x4d, 0x55, 0xc1, 0xf0, 0x57, 0x06, 0xfe, 0x58, 0x66, 0x37, 0xb3, 0x44, 0xaa, 0xc5, 0xbd, + 0xf4, 0xf8, 0x0c, 0xbc, 0x39, 0xc6, 0x71, 0x16, 0x38, 0x03, 0x67, 0x78, 0xf0, 0xe4, 0xd1, 0x71, + 0x2d, 0x74, 0xcd, 0x73, 0x8a, 0x71, 0x2c, 0xcc, 0x2e, 0xfe, 0x05, 0xf8, 0x39, 0xae, 0xd3, 0x58, + 0xe6, 0x98, 0x05, 0xae, 0xfe, 0x84, 0x37, 0x9f, 0x5c, 0xda, 0x94, 0x68, 0x36, 0x85, 0x7f, 0x76, + 0xe0, 0xc1, 0x1e, 0x15, 0x3f, 0x04, 0xb6, 0xd3, 0xb7, 0xf2, 0x04, 0xdb, 0x11, 0x2a, 0xf5, 0x8d, + 0x3c, 0xc1, 0x4a, 0x42, 0x5b, 0x5d, 0x1b, 0x4f, 0xb0, 0x2d, 0xa1, 0x1b, 0x5d, 0x11, 0x4f, 0xb0, + 0x1b, 0xfe, 0x09, 0xf4, 0x7e, 0x2e, 0x50, 0x45, 0x98, 0x05, 0x9e, 0x3e, 0xf9, 0x9d, 0xe6, 0xe4, + 0xef, 0x0b, 0x54, 0xa5, 0xa8, 0xf2, 0xf4, 0x52, 0x5d, 0x4d, 0x53, 0x1a, 0xbd, 0xa6, 0x58, 0x4e, + 0x95, 0xef, 0x99, 0x18, 0xad, 0xad, 0x42, 0xa6, 0x1e, 0xa4, 0xd0, 0x97, 0xe0, 0xca, 0x1d, 0x66, + 0x81, 0xaf, 0xf9, 0x3f, 0x7c, 0x83, 0x18, 0xc7, 0xa3, 0x1d, 0x66, 0xdf, 0x6c, 0x72, 0x55, 0x0a, + 0xbd, 0xfd, 0xf1, 0x33, 0xf0, 0xeb, 0x10, 0xb9, 0xe2, 0x15, 0x96, 0xfa, 0x81, 0xbe, 0xa0, 0x25, + 0xff, 0x08, 0xbc, 0x5b, 0x19, 0x17, 0x46, 0xf8, 0x83, 0x27, 0x47, 0x0d, 0xed, 0x68, 0x17, 0x65, + 0xc2, 0x24, 0xbf, 0xee, 0x7c, 0xc5, 0xc2, 0x1f, 0xc1, 0xa5, 0x10, 0x0f, 0xe1, 0x30, 0xc6, 0x95, + 0x9c, 0x97, 0x27, 0x49, 0xb1, 0x59, 0x64, 0x01, 0x1b, 0x38, 0x43, 0x47, 0xec, 0xc5, 0xf8, 0x7b, + 0xd0, 0x9d, 0x99, 0x6c, 0x67, 0xe0, 0x0c, 0x7d, 0x61, 0x11, 0x7f, 0x08, 0x5e, 0x2c, 0x67, 0x18, + 0x5b, 0x8b, 0x1b, 0x10, 0xfe, 0xcd, 0xc8, 0x80, 0xa6, 0x28, 0x2d, 0x63, 0x98, 0x67, 0xbf, 0x0f, + 0x7d, 0x2a, 0xd8, 0xcb, 0x5b, 0xa9, 0xac, 0x39, 0x7a, 0x84, 0xaf, 0xa5, 0xe2, 0x9f, 0x43, 0x57, + 0x5f, 0xef, 0x0e, 0x83, 0x54, 0x74, 0xd7, 0x94, 0x17, 0x76, 0x5b, 0x2d, 0xb3, 0xdb, 0x92, 0xb9, + 0xbe, 0x92, 0xd7, 0xba, 0x12, 0x59, 0x8f, 0xea, 0x55, 0xea, 0x2a, 0xdd, 0xc9, 0x6c, 0xaa, 0x6a, + 0x76, 0x85, 0x57, 0xf0, 0x60, 0xef, 0xc4, 0xfa, 0x24, 0xb6, 0x7f, 0x52, 0x23, 0xb5, 0x6f, 0xa5, + 0xa5, 0xe6, 0xcb, 0x30, 0xc6, 0x79, 0x8e, 0x0b, 0xad, 0x4a, 0x5f, 0xd4, 0x38, 0xfc, 0x9d, 0x35, + 0xbc, 0xfa, 0x3c, 0x6a, 0xaf, 0x79, 0xb2, 0x5e, 0xcb, 0xcd, 0xc2, 0x52, 0x57, 0x90, 0x74, 0x5b, + 0xcc, 0x2c, 0x75, 0x67, 0x31, 0x23, 0xac, 0x52, 0xab, 0x73, 0x47, 0xa5, 0x7c, 0x00, 0x07, 0x6b, + 0x94, 0x59, 0xa1, 0x70, 0x8d, 0x9b, 0xdc, 0x4a, 0xd0, 0x0e, 0xf1, 0x47, 0xd0, 0xcb, 0xe5, 0xea, + 0x25, 0x19, 0xc4, 0x68, 0xd1, 0xcd, 0xe5, 0xea, 0x0c, 0x4b, 0xfe, 0x01, 0xf8, 0xcb, 0x08, 0xe3, + 0x85, 0x4e, 0x19, 0xdb, 0xf6, 0x75, 0xe0, 0x0c, 0xcb, 0xf0, 0x0f, 0x06, 0xdd, 0x29, 0xaa, 0x5b, + 0x54, 0xf7, 0xea, 0xe9, 0xf6, 0x3c, 0x73, 0xfe, 0x67, 0x9e, 0xb9, 0x77, 0xcf, 0x33, 0xaf, 0x99, + 0x67, 0x0f, 0xc1, 0x9b, 0xaa, 0xf9, 0x64, 0xac, 0x6f, 0xe4, 0x08, 0x03, 0xc8, 0x79, 0xa3, 0x79, + 0x1e, 0xdd, 0xa2, 0x1d, 0x72, 0x16, 0x85, 0xbf, 0x31, 0xe8, 0x9e, 0xcb, 0x32, 0x29, 0xf2, 0xd7, + 0x1c, 0x36, 0x80, 0x83, 0x51, 0x9a, 0xc6, 0xd1, 0x5c, 0xe6, 0x51, 0xb2, 0xb1, 0xb7, 0x6d, 0x87, + 0x68, 0xc7, 0xf3, 0x96, 0x76, 0xe6, 0xde, 0xed, 0x10, 0xb5, 0xd1, 0xa9, 0x1e, 0x55, 0x66, 0xee, + 0xb4, 0xda, 0xc8, 0x4c, 0x28, 0x9d, 0xa4, 0x07, 0x8e, 0x8a, 0x3c, 0x59, 0xc6, 0xc9, 0x56, 0xbf, + 0xa4, 0x2f, 0x6a, 0x1c, 0xfe, 0xc3, 0xc0, 0x7d, 0x5b, 0x23, 0xe8, 0x10, 0x58, 0x64, 0x0b, 0xc9, + 0xa2, 0x7a, 0x20, 0xf5, 0x5a, 0x03, 0x29, 0x80, 0x5e, 0xa9, 0xe4, 0x66, 0x85, 0x59, 0xd0, 0xd7, + 0xfd, 0x5d, 0x41, 0x9d, 0xd1, 0x3d, 0x62, 0x26, 0x91, 0x2f, 0x2a, 0x58, 0x7b, 0x1e, 0x1a, 0xcf, + 0x87, 0x7f, 0x31, 0xf0, 0x6a, 0xe7, 0x9e, 0xee, 0x3b, 0xf7, 0xb4, 0x71, 0xee, 0xf8, 0xa4, 0x72, + 0xee, 0xf8, 0x84, 0xb0, 0xb8, 0xa8, 0x9c, 0x2b, 0x2e, 0x48, 0xb5, 0x67, 0x2a, 0x29, 0xd2, 0x93, + 0xd2, 0xc8, 0xeb, 0x8b, 0x1a, 0x53, 0xb9, 0x7f, 0xb8, 0x41, 0x65, 0xdf, 0xec, 0x0b, 0x8b, 0xc8, + 0x1c, 0xe7, 0xba, 0xab, 0xcd, 0x2b, 0x0d, 0xe0, 0x1f, 0x83, 0x27, 0xe8, 0x15, 0xfa, 0xa9, 0x7b, + 0x02, 0xe9, 0xb0, 0x30, 0xd9, 0xf0, 0xa9, 0xdd, 0x46, 0x2c, 0x57, 0x69, 0x8a, 0xca, 0x7a, 0xda, + 0x00, 0xcd, 0x9d, 0x6c, 0xd1, 0x8c, 0x23, 0x47, 0x18, 0x10, 0xfe, 0x04, 0xfe, 0x28, 0x46, 0x95, + 0x8b, 0x22, 0x7e, 0x7d, 0x88, 0x71, 0x70, 0xbf, 0x9d, 0x7e, 0xf7, 0xa2, 0xea, 0x04, 0x5a, 0x37, + 0xfe, 0x75, 0xfe, 0xe3, 0xdf, 0x33, 0x99, 0xca, 0xc9, 0x58, 0x17, 0xd6, 0x11, 0x16, 0x85, 0x9f, + 0x82, 0x4b, 0x7d, 0xd2, 0x62, 0x76, 0xdf, 0xd4, 0x63, 0xb3, 0xae, 0xfe, 0x0f, 0x79, 0xfa, 0x6f, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x0d, 0xf5, 0x54, 0xe9, 0x99, 0x08, 0x00, 0x00, } diff --git a/bolt/internal/internal.proto b/bolt/internal/internal.proto index 18a2a77468..ec979b6c95 100644 --- a/bolt/internal/internal.proto +++ b/bolt/internal/internal.proto @@ -35,7 +35,8 @@ message DashboardCell { message Axis { repeated int64 legacyBounds = 1; // legacyBounds are an ordered 2-tuple consisting of lower and upper axis extents, respectively - repeated string bounds = 2; // bounds are an arbitrary list of client-defined bounds. + repeated string bounds = 2; // bounds are an arbitrary list of client-defined bounds. + string label = 3; // label is a description of this axis } message Template { diff --git a/bolt/internal/internal_test.go b/bolt/internal/internal_test.go index 82abcbad2d..a2952148a6 100644 --- a/bolt/internal/internal_test.go +++ b/bolt/internal/internal_test.go @@ -129,6 +129,7 @@ func Test_MarshalDashboard(t *testing.T) { Axes: map[string]chronograf.Axis{ "y": chronograf.Axis{ Bounds: []string{"0", "3", "1-7", "foo"}, + Label: "foo", }, }, Type: "line", diff --git a/chronograf.go b/chronograf.go index b8dcd4537c..2b69c578c5 100644 --- a/chronograf.go +++ b/chronograf.go @@ -568,8 +568,9 @@ type Dashboard struct { // Axis represents the visible extents of a visualization type Axis struct { - Bounds []string `json:"bounds"` // bounds are an arbitrary list of client-defined strings that specify the viewport for a cell - LegacyBounds [2]int64 `json:"-"` // legacy bounds are for testing a migration from an earlier version of axis + Bounds []string `json:"bounds"` // bounds are an arbitrary list of client-defined strings that specify the viewport for a cell + LegacyBounds [2]int64 `json:"-"` // legacy bounds are for testing a migration from an earlier version of axis + Label string `json:"label,omitempty"` // label is a description of this Axis } // DashboardCell holds visual and query information for a cell diff --git a/server/dashboards_test.go b/server/dashboards_test.go index f7cbaa7472..ff740ba001 100644 --- a/server/dashboards_test.go +++ b/server/dashboards_test.go @@ -226,6 +226,7 @@ func Test_newDashboardResponse(t *testing.T) { }, "y": chronograf.Axis{ Bounds: []string{"2", "95"}, + Label: "foo", }, }, }, @@ -272,6 +273,7 @@ func Test_newDashboardResponse(t *testing.T) { }, "y": chronograf.Axis{ Bounds: []string{"2", "95"}, + Label: "foo", }, "y2": chronograf.Axis{ Bounds: []string{}, From e1ab849b6334672f2d0cedcd382b8cbd1a103bc4 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 2 Aug 2017 08:45:09 -0700 Subject: [PATCH 60/93] Prettier --- ui/src/dashboards/components/GraphTypeSelector.js | 4 +++- ui/src/dashboards/components/Ranger.js | 6 +++--- ui/src/dashboards/containers/DashboardPage.js | 3 ++- ui/src/shared/components/GrooveKnob.js | 4 +++- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/ui/src/dashboards/components/GraphTypeSelector.js b/ui/src/dashboards/components/GraphTypeSelector.js index 15561447a8..92ff5313ee 100644 --- a/ui/src/dashboards/components/GraphTypeSelector.js +++ b/ui/src/dashboards/components/GraphTypeSelector.js @@ -15,7 +15,9 @@ const GraphTypeSelector = ({selectedGraphType, onSelectGraphType}) => >
    onSelectGraphType(graphType.type)}> {graphType.graphic} -

    {graphType.menuOption}

    +

    + {graphType.menuOption} +

    )} diff --git a/ui/src/dashboards/components/Ranger.js b/ui/src/dashboards/components/Ranger.js index 70cd9aeb9d..53d0554719 100644 --- a/ui/src/dashboards/components/Ranger.js +++ b/ui/src/dashboards/components/Ranger.js @@ -22,17 +22,17 @@ const Ranger = ({onSetRange, axes}) => { />
    */}
    - + { console.log(leftValue, rightValue) // onSetRange() }} - rightValue={Number(min)} + rightValue={min} />
    - + -
    {leftLabel}
    +
    + {leftLabel} +
    Date: Wed, 2 Aug 2017 09:38:15 -0700 Subject: [PATCH 61/93] Revert "WIP Pass values from GrooveKnob to parent" This reverts commit c0cc7308576968f5c72ca30f8b02b8df888d3d7b. --- ui/src/dashboards/components/Ranger.js | 27 ++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/ui/src/dashboards/components/Ranger.js b/ui/src/dashboards/components/Ranger.js index 53d0554719..19c3fbd0cb 100644 --- a/ui/src/dashboards/components/Ranger.js +++ b/ui/src/dashboards/components/Ranger.js @@ -1,8 +1,6 @@ import React, {PropTypes} from 'react' import _ from 'lodash' -import GrooveKnob from 'shared/components/GrooveKnob' - // TODO: add logic for for Prefix, Suffix, Scale, and Multiplier const Ranger = ({onSetRange, axes}) => { const min = _.get(axes, ['y', 'bounds', '0'], '') @@ -22,14 +20,23 @@ const Ranger = ({onSetRange, axes}) => { />
    */}
    - - { - console.log(leftValue, rightValue) - // onSetRange() - }} - rightValue={min} - /> + +
    +
    auto
    +
    +
    +
    + +
    From 31756ec3614cdb999e7bc19bef92442c20dff5ea Mon Sep 17 00:00:00 2001 From: Jared Scheib Date: Wed, 2 Aug 2017 09:39:55 -0700 Subject: [PATCH 62/93] Revert "Wire up GrooveKnob to be fully functional" This reverts commit 69dbd0e988a8350ad32731a35fb7353390cf30d2. --- ui/src/style/components/one-or-any.scss | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/src/style/components/one-or-any.scss b/ui/src/style/components/one-or-any.scss index 6580d5cbd1..7b23d3bc7a 100644 --- a/ui/src/style/components/one-or-any.scss +++ b/ui/src/style/components/one-or-any.scss @@ -24,7 +24,7 @@ background-color 0.25s ease, color 0.25s ease; } -.one-or-any--switch { +.one-or-any--toggle { display: flex; align-items: center; border: 2px solid $g5-pepper; @@ -88,11 +88,11 @@ border-radius: 0 4px 4px 0; font-family: $code-font; } -// When using right value -.one-or-any.use-right-value { +// Toggled state +.one-or-any.toggled { .one-or-any--groove-knob:after {transform: translate(0%,-50%);} // Fade out left, fade in right - .one-or-any--switch:before {opacity: 0;} + .one-or-any--toggle:before {opacity: 0;} // Make auto look disabled .one-or-any--auto { background-color: $g3-castle; From a7c035fdceed4425ab5ea1090add66ee49ec2313 Mon Sep 17 00:00:00 2001 From: Jared Scheib Date: Wed, 2 Aug 2017 09:40:22 -0700 Subject: [PATCH 63/93] Revert "Rename classes and element" This reverts commit b7bb9cb33905afabfcd45baa38aa924015b272db. --- ui/src/dashboards/components/Ranger.js | 4 +- ui/src/style/components/one-or-any.scss | 60 +++++++++++++------------ 2 files changed, 33 insertions(+), 31 deletions(-) diff --git a/ui/src/dashboards/components/Ranger.js b/ui/src/dashboards/components/Ranger.js index 19c3fbd0cb..b5615c6b8a 100644 --- a/ui/src/dashboards/components/Ranger.js +++ b/ui/src/dashboards/components/Ranger.js @@ -23,8 +23,8 @@ const Ranger = ({onSetRange, axes}) => {
    auto
    -
    -
    +
    +
    div { + margin: 0 10px; + z-index: 3; + width: 28px; + height: 8px; + border-radius: 4px; + background-color: $g6-smoke; + position: relative; + + // Dot + &:after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + width: 14px; + height: 14px; + border-radius: 50%; + background-color: $c-pool; + transition: + background-color 0.25s ease, + transform 0.25s ease; + transform: translate(-100%,-50%); + } + } + // Background Gradients &:before, &:after { content: ''; @@ -55,32 +82,7 @@ &:hover { cursor: pointer; - .one-or-any--groove-knob:after {background-color: $c-laser;} - } -} -.one-or-any--groove-knob { - margin: 0 10px; - z-index: 3; - width: 28px; - height: 8px; - border-radius: 4px; - background-color: $g6-smoke; - position: relative; - - // Knob - &:after { - content: ''; - position: absolute; - top: 50%; - left: 50%; - width: 14px; - height: 14px; - border-radius: 50%; - background-color: $c-pool; - transition: - background-color 0.25s ease, - transform 0.25s ease; - transform: translate(-100%,-50%); + > div:after {background-color: $c-laser;} } } // Customize form input @@ -90,9 +92,9 @@ } // Toggled state .one-or-any.toggled { - .one-or-any--groove-knob:after {transform: translate(0%,-50%);} + .one-or-any--switch > div:after {transform: translate(0%,-50%);} // Fade out left, fade in right - .one-or-any--toggle:before {opacity: 0;} + .one-or-any--switch:before {opacity: 0;} // Make auto look disabled .one-or-any--auto { background-color: $g3-castle; From 27bc87eac54fc90421cdbe41978835c1c2527bcf Mon Sep 17 00:00:00 2001 From: Jared Scheib Date: Wed, 2 Aug 2017 09:40:31 -0700 Subject: [PATCH 64/93] Revert "WIP "one or any" component" This reverts commit 3897228e30976ff055615e6cb75d0263616fa559. --- ui/src/dashboards/components/Ranger.js | 27 +++--- ui/src/style/chronograf.scss | 1 - ui/src/style/components/one-or-any.scss | 104 ------------------------ 3 files changed, 10 insertions(+), 122 deletions(-) delete mode 100644 ui/src/style/components/one-or-any.scss diff --git a/ui/src/dashboards/components/Ranger.js b/ui/src/dashboards/components/Ranger.js index b5615c6b8a..09ce134005 100644 --- a/ui/src/dashboards/components/Ranger.js +++ b/ui/src/dashboards/components/Ranger.js @@ -20,23 +20,16 @@ const Ranger = ({onSetRange, axes}) => { />
    */}
    - -
    -
    auto
    -
    -
    -
    - -
    + +
    diff --git a/ui/src/style/chronograf.scss b/ui/src/style/chronograf.scss index c6547693cf..55094c8cf9 100644 --- a/ui/src/style/chronograf.scss +++ b/ui/src/style/chronograf.scss @@ -36,7 +36,6 @@ @import 'components/graph'; @import 'components/input-tag-list'; @import 'components/newsfeed'; -@import 'components/one-or-any'; @import 'components/page-header-dropdown'; @import 'components/page-header-editable'; @import 'components/page-spinner'; diff --git a/ui/src/style/components/one-or-any.scss b/ui/src/style/components/one-or-any.scss deleted file mode 100644 index 82a941e056..0000000000 --- a/ui/src/style/components/one-or-any.scss +++ /dev/null @@ -1,104 +0,0 @@ -/* - One or Any Input - ------------------------------------------------------------------------------ - User can toggle between a single value or any value -*/ -.one-or-any { - display: flex; - align-items: stretch; - flex-wrap: nowrap; -} -.one-or-any--auto { - border: 2px solid $g5-pepper; - background-color: $g2-kevlar; - color: $c-pool; - font-family: $code-font; - padding: 0 11px; - border-radius: 4px 0 0 4px; - line-height: 24px; - font-size: 13px; - font-weight: 500; - cursor: default; - @include no-user-select(); - transition: - background-color 0.25s ease, - color 0.25s ease; -} -.one-or-any--switch { - display: flex; - align-items: center; - border: 2px solid $g5-pepper; - border-left: 0; - border-right: 0; - position: relative; - - // Tray - > div { - margin: 0 10px; - z-index: 3; - width: 28px; - height: 8px; - border-radius: 4px; - background-color: $g6-smoke; - position: relative; - - // Dot - &:after { - content: ''; - position: absolute; - top: 50%; - left: 50%; - width: 14px; - height: 14px; - border-radius: 50%; - background-color: $c-pool; - transition: - background-color 0.25s ease, - transform 0.25s ease; - transform: translate(-100%,-50%); - } - } - - // Background Gradients - &:before, &:after { - content: ''; - display: block; - position: absolute; - width: 100%; - height: 100%; - transition: opacity 0.25s ease; - } - // Left - &:before { - z-index: 2; - @include gradient-h($g2-kevlar,$g3-castle); - opacity: 1; - } - // Right - &:after { - @include gradient-h($g3-castle,$g2-kevlar); - z-index: 1; - } - - &:hover { - cursor: pointer; - > div:after {background-color: $c-laser;} - } -} -// Customize form input -.one-or-any > input.form-control { - border-radius: 0 4px 4px 0; - font-family: $code-font; -} -// Toggled state -.one-or-any.toggled { - .one-or-any--switch > div:after {transform: translate(0%,-50%);} - // Fade out left, fade in right - .one-or-any--switch:before {opacity: 0;} - // Make auto look disabled - .one-or-any--auto { - background-color: $g3-castle; - color: $g8-storm; - font-style: italic; - } -} From 640afb3f554d4a2d1ffaf937fee97ec91cbcb685 Mon Sep 17 00:00:00 2001 From: Jared Scheib Date: Wed, 2 Aug 2017 09:41:35 -0700 Subject: [PATCH 65/93] Revert GrooveKnob addition --- ui/src/shared/components/GrooveKnob.js | 85 -------------------------- 1 file changed, 85 deletions(-) delete mode 100644 ui/src/shared/components/GrooveKnob.js diff --git a/ui/src/shared/components/GrooveKnob.js b/ui/src/shared/components/GrooveKnob.js deleted file mode 100644 index 890c7d9a6c..0000000000 --- a/ui/src/shared/components/GrooveKnob.js +++ /dev/null @@ -1,85 +0,0 @@ -import React, {Component, PropTypes} from 'react' -import classnames from 'classnames' - -class GrooveKnob extends Component { - constructor(props) { - super(props) - - const {leftValue, rightValue} = props - - this.state = { - leftValue, - rightValue, - } - - this.handleChangeRightValue = ::this.handleChangeRightValue - this.handleToggleLeftValue = ::this.handleToggleLeftValue - this.handleSetValues = ::this.handleSetValues - } - - handleChangeRightValue(newValue) { - this.setState({rightValue: newValue}, this.handleSetValues) - } - - handleToggleLeftValue() { - const {leftValue} = this.state - - this.setState({leftValue: !leftValue}, this.handleSetValues) - } - - handleSetValues() { - const {onSetValues} = this.props - const {leftValue, rightValue} = this.state - - onSetValues({leftValue, rightValue}) - } - - render() { - const {leftLabel, rightLabel} = this.props - const {leftValue: useLeftValue, rightValue} = this.state - - return ( -
    -
    - {leftLabel} -
    -
    -
    -
    - this.handleChangeRightValue(e.target.value)} - placeholder={rightLabel} - disabled={useLeftValue} - /> -
    - ) - } -} - -GrooveKnob.defaultProps = { - leftLabel: 'auto', - leftValue: true, - rightLabel: 'Custom Value', - rightValue: null, -} -const {bool, func, number, string} = PropTypes - -GrooveKnob.propTypes = { - leftLabel: string, - leftValue: bool, - rightLabel: string, - rightValue: number, - onSetValues: func.isRequired, -} - -export default GrooveKnob From a6be00d4f36aeba04621f762a56c81d9b20f850e Mon Sep 17 00:00:00 2001 From: Tim Raymond Date: Wed, 2 Aug 2017 13:07:27 -0400 Subject: [PATCH 66/93] Remove omitempty from Axis Label If the key is always present in the response, it's easier for the front end to perform tests on contents of its key. --- chronograf.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/chronograf.go b/chronograf.go index 336dc2d0c5..319065befc 100644 --- a/chronograf.go +++ b/chronograf.go @@ -569,9 +569,9 @@ type Dashboard struct { // Axis represents the visible extents of a visualization type Axis struct { - Bounds []string `json:"bounds"` // bounds are an arbitrary list of client-defined strings that specify the viewport for a cell - LegacyBounds [2]int64 `json:"-"` // legacy bounds are for testing a migration from an earlier version of axis - Label string `json:"label,omitempty"` // label is a description of this Axis + Bounds []string `json:"bounds"` // bounds are an arbitrary list of client-defined strings that specify the viewport for a cell + LegacyBounds [2]int64 `json:"-"` // legacy bounds are for testing a migration from an earlier version of axis + Label string `json:"label"` // label is a description of this Axis } // DashboardCell holds visual and query information for a cell From 4062a086ae68c36688ea46680ffa8c5aa698a386 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 2 Aug 2017 12:47:57 -0700 Subject: [PATCH 67/93] Fix props error --- ui/src/data_explorer/components/VisView.js | 2 +- ui/src/data_explorer/components/Visualization.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/src/data_explorer/components/VisView.js b/ui/src/data_explorer/components/VisView.js index 036d554c1a..9e717c6f14 100644 --- a/ui/src/data_explorer/components/VisView.js +++ b/ui/src/data_explorer/components/VisView.js @@ -52,7 +52,7 @@ const {arrayOf, func, number, shape, string} = PropTypes VisView.propTypes = { view: string.isRequired, - axes: shape().isRequired, + axes: shape(), queries: arrayOf(shape()).isRequired, cellType: string, templates: arrayOf(shape()), diff --git a/ui/src/data_explorer/components/Visualization.js b/ui/src/data_explorer/components/Visualization.js index 26c212b394..7466ff69ac 100644 --- a/ui/src/data_explorer/components/Visualization.js +++ b/ui/src/data_explorer/components/Visualization.js @@ -53,6 +53,7 @@ const Visualization = React.createClass({ getDefaultProps() { return { cellName: '', + cellType: '', } }, From 1fa004664b02e71d802f9ec6d87af51699a2d394 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 2 Aug 2017 14:08:07 -0700 Subject: [PATCH 68/93] Add ability for users to specify labels --- .../components/CellEditorOverlay.js | 29 ++++++++++++++++--- .../dashboards/components/DisplayOptions.js | 4 ++- ui/src/dashboards/components/Ranger.js | 17 +++++++---- ui/src/shared/components/Dygraph.js | 4 +++ ui/src/shared/components/LineGraph.js | 6 ++-- 5 files changed, 45 insertions(+), 15 deletions(-) diff --git a/ui/src/dashboards/components/CellEditorOverlay.js b/ui/src/dashboards/components/CellEditorOverlay.js index 595642a60c..5259953203 100644 --- a/ui/src/dashboards/components/CellEditorOverlay.js +++ b/ui/src/dashboards/components/CellEditorOverlay.js @@ -23,17 +23,15 @@ class CellEditorOverlay extends Component { super(props) this.queryStateReducer = ::this.queryStateReducer - this.handleAddQuery = ::this.handleAddQuery this.handleDeleteQuery = ::this.handleDeleteQuery - this.handleSaveCell = ::this.handleSaveCell - this.handleSelectGraphType = ::this.handleSelectGraphType this.handleSelectDisplayOptions = ::this.handleSelectDisplayOptions this.handleSetActiveQueryIndex = ::this.handleSetActiveQueryIndex this.handleEditRawText = ::this.handleEditRawText this.handleSetRange = ::this.handleSetRange + this.handleSetLabel = ::this.handleSetLabel const {cell: {name, type, queries, axes}} = props @@ -47,10 +45,24 @@ class CellEditorOverlay extends Component { queriesWorkingDraft, activeQueryIndex: 0, isDisplayOptionsTabOpen: false, - axes, + axes: this.setDefaultLabels(axes, queries), } } + setDefaultLabels(axes, queries) { + if (!queries.length) { + return axes + } + + if (axes.y.label) { + return axes + } + + const q = queries[0].queryConfig + const label = q.rawText ? '' : `${q.measurement}.${q.fields[0].field}` + return {...axes, y: {...axes.y, label}} + } + componentWillReceiveProps(nextProps) { const {status, queryID} = this.props.queryStatus const nextStatus = nextProps.queryStatus @@ -86,6 +98,14 @@ class CellEditorOverlay extends Component { e.preventDefault() } + handleSetLabel(e) { + const {label} = e.target.form + const {axes} = this.state + + this.setState({axes: {...axes, y: {label: label.value}}}) + e.preventDefault() + } + handleAddQuery(options) { const newQuery = Object.assign({}, defaultQueryConfig(uuid.v4()), options) const nextQueries = this.state.queriesWorkingDraft.concat(newQuery) @@ -223,6 +243,7 @@ class CellEditorOverlay extends Component { selectedGraphType={cellWorkingType} onSelectGraphType={this.handleSelectGraphType} onSetRange={this.handleSetRange} + onSetLabel={this.handleSetLabel} axes={axes} /> : @@ -14,7 +15,7 @@ const DisplayOptions = ({ selectedGraphType={selectedGraphType} onSelectGraphType={onSelectGraphType} /> - +
    const {func, shape, string} = PropTypes @@ -23,6 +24,7 @@ DisplayOptions.propTypes = { selectedGraphType: string.isRequired, onSelectGraphType: func.isRequired, onSetRange: func.isRequired, + onSetLabel: func.isRequired, axes: shape({}).isRequired, } diff --git a/ui/src/dashboards/components/Ranger.js b/ui/src/dashboards/components/Ranger.js index 09ce134005..93353f8261 100644 --- a/ui/src/dashboards/components/Ranger.js +++ b/ui/src/dashboards/components/Ranger.js @@ -2,23 +2,26 @@ import React, {PropTypes} from 'react' import _ from 'lodash' // TODO: add logic for for Prefix, Suffix, Scale, and Multiplier -const Ranger = ({onSetRange, axes}) => { +const Ranger = ({onSetRange, onSetLabel, axes}) => { const min = _.get(axes, ['y', 'bounds', '0'], '') const max = _.get(axes, ['y', 'bounds', '1'], '') + const label = _.get(axes, ['y', 'label'], '') return (
    Y Axis Controls
    - {/*
    - +
    + -
    */} +
    { ) } -const {array, func, shape} = PropTypes +const {arrayOf, func, shape, string} = PropTypes Ranger.propTypes = { onSetRange: func.isRequired, + onSetLabel: func.isRequired, axes: shape({ y: shape({ - bounds: array, + bounds: arrayOf(string), + label: string, }), }).isRequired, } diff --git a/ui/src/shared/components/Dygraph.js b/ui/src/shared/components/Dygraph.js index 8b80312c4d..9d597ef239 100644 --- a/ui/src/shared/components/Dygraph.js +++ b/ui/src/shared/components/Dygraph.js @@ -73,6 +73,7 @@ export default class Dygraph extends Component { const yAxis = _.get(axes, ['y', 'bounds'], [null, null]) const y2Axis = _.get(axes, ['y2', 'bounds'], undefined) + const ylabel = _.get(axes, ['y', 'label'], '') const defaultOptions = { plugins: [ @@ -93,6 +94,7 @@ export default class Dygraph extends Component { hideOverlayOnMouseOut: false, colors: finalLineColors, series: dygraphSeries, + ylabel, axes: { y: { valueRange: getRange(timeSeries, yAxis, ruleValues), @@ -254,11 +256,13 @@ export default class Dygraph extends Component { const y = _.get(axes, ['y', 'bounds'], [null, null]) const y2 = _.get(axes, ['y2', 'bounds'], undefined) + const ylabel = _.get(axes, ['y', 'label'], '') const timeSeries = this.getTimeSeries() const updateOptions = { labels, file: timeSeries, + ylabel, axes: { y: { valueRange: getRange(timeSeries, y, ruleValues), diff --git a/ui/src/shared/components/LineGraph.js b/ui/src/shared/components/LineGraph.js index e92d4dfe8f..dcf93fab15 100644 --- a/ui/src/shared/components/LineGraph.js +++ b/ui/src/shared/components/LineGraph.js @@ -2,7 +2,6 @@ import React, {PropTypes} from 'react' import Dygraph from 'shared/components/Dygraph' import classnames from 'classnames' import shallowCompare from 'react-addons-shallow-compare' -import _ from 'lodash' import timeSeriesToDygraph from 'utils/timeSeriesToDygraph' import lastValues from 'shared/parsing/lastValues' @@ -18,9 +17,11 @@ export default React.createClass({ axes: shape({ y: shape({ bounds: array, + label: string, }), y2: shape({ bounds: array, + label: string, }), }), title: string, @@ -93,7 +94,6 @@ export default React.createClass({ overrideLineColors, title, underlayCallback, - queries, showSingleStat, displayOptions, ruleValues, @@ -124,8 +124,6 @@ export default React.createClass({ axisLabelWidth: 60, drawAxesAtZero: true, underlayCallback, - ylabel: _.get(queries, ['0', 'label'], ''), - y2label: _.get(queries, ['1', 'label'], ''), ...displayOptions, } From cc34b9c978cb16fcfed59f489c5f691c51c69fdb Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 2 Aug 2017 14:09:08 -0700 Subject: [PATCH 69/93] Stop adding labels to queries --- ui/src/dashboards/components/CellEditorOverlay.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/ui/src/dashboards/components/CellEditorOverlay.js b/ui/src/dashboards/components/CellEditorOverlay.js index 5259953203..325837e8d6 100644 --- a/ui/src/dashboards/components/CellEditorOverlay.js +++ b/ui/src/dashboards/components/CellEditorOverlay.js @@ -132,12 +132,10 @@ class CellEditorOverlay extends Component { const queries = queriesWorkingDraft.map(q => { const timeRange = q.range || {upper: null, lower: ':dashboardTime:'} const query = q.rawText || buildInfluxQLQuery(timeRange, q) - const label = q.rawText ? '' : `${q.measurement}.${q.fields[0].field}` return { queryConfig: q, query, - label, } }) From a9249e04e0b9e65d8d0fc7242855b8611ee21263 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 2 Aug 2017 14:15:00 -0700 Subject: [PATCH 70/93] Remove TODO --- ui/src/dashboards/actions/index.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ui/src/dashboards/actions/index.js b/ui/src/dashboards/actions/index.js index d128589af9..205d017cb2 100644 --- a/ui/src/dashboards/actions/index.js +++ b/ui/src/dashboards/actions/index.js @@ -184,8 +184,7 @@ export const putDashboardByID = dashboardID => async (dispatch, getState) => { export const updateDashboardCell = (dashboard, cell) => async dispatch => { try { const {data} = await updateDashboardCellAJAX(cell) - // TODO: remove yRanges when server persists the ranges - dispatch(syncDashboardCell(dashboard, {...data, yRanges: cell.yRanges})) + dispatch(syncDashboardCell(dashboard, data)) } catch (error) { console.error(error) dispatch(errorThrown(error)) From 91622930711ef95cd1f14d9b7cc5944be94370a1 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 2 Aug 2017 14:16:14 -0700 Subject: [PATCH 71/93] Update style logic to use axes labels --- ui/src/shared/components/LineGraph.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ui/src/shared/components/LineGraph.js b/ui/src/shared/components/LineGraph.js index dcf93fab15..cbeeac5111 100644 --- a/ui/src/shared/components/LineGraph.js +++ b/ui/src/shared/components/LineGraph.js @@ -153,10 +153,12 @@ export default React.createClass({ roundedValue = Math.round(+lastValue * precision) / precision } + const isLabelSet = !!axes.y.label || !!axes.y2.label + return (
    From e863165019ed06d4b93bab3293f792e5a516ae9b Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 2 Aug 2017 14:26:05 -0700 Subject: [PATCH 72/93] Cleanup classname --- ui/src/shared/components/LineGraph.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/ui/src/shared/components/LineGraph.js b/ui/src/shared/components/LineGraph.js index cbeeac5111..65257c3e65 100644 --- a/ui/src/shared/components/LineGraph.js +++ b/ui/src/shared/components/LineGraph.js @@ -153,15 +153,11 @@ export default React.createClass({ roundedValue = Math.round(+lastValue * precision) / precision } - const isLabelSet = !!axes.y.label || !!axes.y2.label + const isLabelSet = + !!axes.y.label || !!axes.y2.label ? 'graph--hasYLabel' : '' return ( -
    +
    {isRefreshing ? this.renderSpinner() : null} Date: Wed, 2 Aug 2017 15:07:54 -0700 Subject: [PATCH 73/93] Move building of label to dygraph component --- .../components/CellEditorOverlay.js | 4 +++- ui/src/shared/components/Dygraph.js | 23 +++++++++++++++---- ui/src/shared/components/LineGraph.js | 10 +++++--- ui/src/shared/presenters/index.js | 6 +++++ 4 files changed, 34 insertions(+), 9 deletions(-) diff --git a/ui/src/dashboards/components/CellEditorOverlay.js b/ui/src/dashboards/components/CellEditorOverlay.js index 325837e8d6..584ce2118e 100644 --- a/ui/src/dashboards/components/CellEditorOverlay.js +++ b/ui/src/dashboards/components/CellEditorOverlay.js @@ -17,6 +17,7 @@ import {getQueryConfig} from 'shared/apis' import {removeUnselectedTemplateValues} from 'src/dashboards/constants' import {OVERLAY_TECHNOLOGY} from 'shared/constants/classNames' import {MINIMUM_HEIGHTS, INITIAL_HEIGHTS} from 'src/data_explorer/constants' +import {buildYLabel} from 'shared/presenters' class CellEditorOverlay extends Component { constructor(props) { @@ -59,7 +60,8 @@ class CellEditorOverlay extends Component { } const q = queries[0].queryConfig - const label = q.rawText ? '' : `${q.measurement}.${q.fields[0].field}` + const label = buildYLabel(q) + return {...axes, y: {...axes.y, label}} } diff --git a/ui/src/shared/components/Dygraph.js b/ui/src/shared/components/Dygraph.js index 9d597ef239..7690884e68 100644 --- a/ui/src/shared/components/Dygraph.js +++ b/ui/src/shared/components/Dygraph.js @@ -9,6 +9,7 @@ import getRange from 'shared/parsing/getRangeForDygraph' import {LINE_COLORS, multiColumnBarPlotter} from 'src/shared/graphs/helpers' import DygraphLegend from 'src/shared/components/DygraphLegend' +import {buildYLabel} from 'shared/presenters' export default class Dygraph extends Component { constructor(props) { @@ -35,6 +36,7 @@ export default class Dygraph extends Component { this.handleHideLegend = ::this.handleHideLegend this.handleToggleFilter = ::this.handleToggleFilter this.visibility = ::this.visibility + this.getLabel = ::this.getLabel } static defaultProps = { @@ -50,6 +52,18 @@ export default class Dygraph extends Component { return timeSeries.length ? timeSeries : [[0]] } + getLabel(axis) { + const {axes, queries} = this.props + const label = _.get(axes, [axis, 'label'], '') + const queryConfig = _.get(queries, ['0', 'queryConfig'], false) + + if (label || !queryConfig) { + return label + } + + return buildYLabel(queryConfig) + } + componentDidMount() { const timeSeries = this.getTimeSeries() // dygraphSeries is a legend label and its corresponding y-axis e.g. {legendLabel1: 'y', legendLabel2: 'y2'}; @@ -73,7 +87,6 @@ export default class Dygraph extends Component { const yAxis = _.get(axes, ['y', 'bounds'], [null, null]) const y2Axis = _.get(axes, ['y2', 'bounds'], undefined) - const ylabel = _.get(axes, ['y', 'label'], '') const defaultOptions = { plugins: [ @@ -94,7 +107,7 @@ export default class Dygraph extends Component { hideOverlayOnMouseOut: false, colors: finalLineColors, series: dygraphSeries, - ylabel, + ylabel: this.getLabel('y'), axes: { y: { valueRange: getRange(timeSeries, yAxis, ruleValues), @@ -256,13 +269,12 @@ export default class Dygraph extends Component { const y = _.get(axes, ['y', 'bounds'], [null, null]) const y2 = _.get(axes, ['y2', 'bounds'], undefined) - const ylabel = _.get(axes, ['y', 'label'], '') const timeSeries = this.getTimeSeries() const updateOptions = { labels, file: timeSeries, - ylabel, + ylabel: this.getLabel('y'), axes: { y: { valueRange: getRange(timeSeries, y, ruleValues), @@ -369,7 +381,7 @@ export default class Dygraph extends Component { } } -const {array, bool, func, shape, string} = PropTypes +const {array, arrayOf, bool, func, shape, string} = PropTypes Dygraph.propTypes = { axes: shape({ @@ -380,6 +392,7 @@ Dygraph.propTypes = { bounds: array, }), }), + queries: arrayOf(shape), timeSeries: array.isRequired, labels: array.isRequired, options: shape({}), diff --git a/ui/src/shared/components/LineGraph.js b/ui/src/shared/components/LineGraph.js index 65257c3e65..af81ee88b4 100644 --- a/ui/src/shared/components/LineGraph.js +++ b/ui/src/shared/components/LineGraph.js @@ -93,6 +93,7 @@ export default React.createClass({ isBarGraph, overrideLineColors, title, + queries, underlayCallback, showSingleStat, displayOptions, @@ -156,15 +157,18 @@ export default React.createClass({ const isLabelSet = !!axes.y.label || !!axes.y2.label ? 'graph--hasYLabel' : '' + const lineColors = showSingleStat + ? singleStatLineColors + : overrideLineColors + return (
    {isRefreshing ? this.renderSpinner() : null} { + return queryConfig.rawText + ? '' + : `${queryConfig.measurement}.${queryConfig.fields[0].field}` +} From 3c57d0ce36046732ca60cec62f43e35f49a87319 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 2 Aug 2017 15:42:25 -0700 Subject: [PATCH 74/93] Determine if label present via the DOM --- ui/src/shared/components/Dygraph.js | 3 +++ ui/src/shared/components/LineGraph.js | 26 ++++++++++++++++++++++---- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/ui/src/shared/components/Dygraph.js b/ui/src/shared/components/Dygraph.js index 7690884e68..0ddf8bf74f 100644 --- a/ui/src/shared/components/Dygraph.js +++ b/ui/src/shared/components/Dygraph.js @@ -43,6 +43,7 @@ export default class Dygraph extends Component { containerStyle: {}, isGraphFilled: true, overrideLineColors: null, + dygraphRef: () => {}, } getTimeSeries() { @@ -372,6 +373,7 @@ export default class Dygraph extends Component {
    { this.graphRef = r + this.props.dygraphRef(r) }} style={this.props.containerStyle} className="dygraph-child-container" @@ -411,4 +413,5 @@ Dygraph.propTypes = { }), synchronizer: func, setResolution: func, + dygraphRef: func, } diff --git a/ui/src/shared/components/LineGraph.js b/ui/src/shared/components/LineGraph.js index af81ee88b4..1ada791d1d 100644 --- a/ui/src/shared/components/LineGraph.js +++ b/ui/src/shared/components/LineGraph.js @@ -154,19 +154,17 @@ export default React.createClass({ roundedValue = Math.round(+lastValue * precision) / precision } - const isLabelSet = - !!axes.y.label || !!axes.y2.label ? 'graph--hasYLabel' : '' - const lineColors = showSingleStat ? singleStatLineColors : overrideLineColors return ( -
    +
    {isRefreshing ? this.renderSpinner() : null} From 244d8fecd8a16376b5d6a80c556c328158fc7d82 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 2 Aug 2017 15:50:29 -0700 Subject: [PATCH 75/93] Fix clobbering of other axes fields --- ui/src/dashboards/components/CellEditorOverlay.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/dashboards/components/CellEditorOverlay.js b/ui/src/dashboards/components/CellEditorOverlay.js index 584ce2118e..c1a1cfe360 100644 --- a/ui/src/dashboards/components/CellEditorOverlay.js +++ b/ui/src/dashboards/components/CellEditorOverlay.js @@ -104,7 +104,7 @@ class CellEditorOverlay extends Component { const {label} = e.target.form const {axes} = this.state - this.setState({axes: {...axes, y: {label: label.value}}}) + this.setState({axes: {...axes, y: {...axes.y, label: label.value}}}) e.preventDefault() } From 442a7fdc2169a5486a600aefd2c8df9e13d76161 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 2 Aug 2017 16:08:58 -0700 Subject: [PATCH 76/93] Default to having a Ylabel class --- ui/src/shared/components/LineGraph.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/shared/components/LineGraph.js b/ui/src/shared/components/LineGraph.js index 1ada791d1d..3bcd691150 100644 --- a/ui/src/shared/components/LineGraph.js +++ b/ui/src/shared/components/LineGraph.js @@ -199,7 +199,7 @@ export default React.createClass({ const dygraph = this.dygraphRef if (!dygraph) { - return '' + return 'graph--hasYLabel' } const label = dygraph.querySelector('.dygraph-ylabel') From c5ce2e4707f6197ba999ca4db4bf017fa4740ee9 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 2 Aug 2017 16:11:25 -0700 Subject: [PATCH 77/93] Prevent clobbering of label when bounds are set --- ui/src/dashboards/components/CellEditorOverlay.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ui/src/dashboards/components/CellEditorOverlay.js b/ui/src/dashboards/components/CellEditorOverlay.js index c1a1cfe360..48859abe05 100644 --- a/ui/src/dashboards/components/CellEditorOverlay.js +++ b/ui/src/dashboards/components/CellEditorOverlay.js @@ -96,7 +96,9 @@ class CellEditorOverlay extends Component { const {min, max} = e.target.form const {axes} = this.state - this.setState({axes: {...axes, y: {bounds: [min.value, max.value]}}}) + this.setState({ + axes: {...axes, y: {...axes.y, bounds: [min.value, max.value]}}, + }) e.preventDefault() } From 9ffdec7f44416cef8a680fec9d2a79a05b492050 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 2 Aug 2017 16:13:48 -0700 Subject: [PATCH 78/93] Add placeholder to label --- ui/src/dashboards/components/Ranger.js | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/src/dashboards/components/Ranger.js b/ui/src/dashboards/components/Ranger.js index 93353f8261..bad00dfba6 100644 --- a/ui/src/dashboards/components/Ranger.js +++ b/ui/src/dashboards/components/Ranger.js @@ -20,6 +20,7 @@ const Ranger = ({onSetRange, onSetLabel, axes}) => { id="label" value={label} onChange={onSetLabel} + placeholder="auto" />
    From fa0b9d83f6c2e9dbcc222257087bb0a13bc0a730 Mon Sep 17 00:00:00 2001 From: Jared Scheib Date: Wed, 2 Aug 2017 17:05:24 -0700 Subject: [PATCH 79/93] Fix and add second item for changelog update --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 24e76292f9..1b03ba2384 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ 1. [#1799](https://github.com/influxdata/chronograf/pull/1799): Prevent console error spam from Dygraph.synchronize when a dashboard has only one graph ### Features +1. [#1714](https://github.com/influxdata/chronograf/pull/1714): Add ability to edit a dashboard graph's y-axis bounds +1. [#1714](https://github.com/influxdata/chronograf/pull/1714): Add ability to edit a dashboard graph's y-axis label + ### UI Improvements 1. [#1796](https://github.com/influxdata/chronograf/pull/1796): Add spinner to indicate data is being written 1. [#1800](https://github.com/influxdata/chronograf/pull/1800): Embiggen text area for line protocol manual entry in Data Explorer's Write Data overlay @@ -22,9 +25,6 @@ ### Features 1. [#1717](https://github.com/influxdata/chronograf/pull/1717): View server generated TICKscripts -1. [#1681](https://github.com/influxdata/chronograf/pull/1681): Add the ability to select Custom Time Ranges in the Hostpages, Data Explorer, and Dashboards. -1. [#1714](https://github.com/influxdata/chronograf/pull/1714): Add ability for users to set y-axis bounds - 1. [#1681](https://github.com/influxdata/chronograf/pull/1681): Add the ability to select Custom Time Ranges in the Hostpages, Data Explorer, and Dashboards 1. [#1752](https://github.com/influxdata/chronograf/pull/1752): Clarify BoltPath server flag help text by making example the default path 1. [#1738](https://github.com/influxdata/chronograf/pull/1738): Add shared secret JWT authorization to InfluxDB From bbceece7f54bdd9e147dd7ab769af433cc5428b4 Mon Sep 17 00:00:00 2001 From: Jared Scheib Date: Wed, 2 Aug 2017 18:08:52 -0700 Subject: [PATCH 80/93] Add line spacing, reorder import --- ui/src/dashboards/components/CellEditorOverlay.js | 3 ++- ui/src/dashboards/components/GraphTypeSelector.js | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ui/src/dashboards/components/CellEditorOverlay.js b/ui/src/dashboards/components/CellEditorOverlay.js index 48859abe05..2c1ae67a0d 100644 --- a/ui/src/dashboards/components/CellEditorOverlay.js +++ b/ui/src/dashboards/components/CellEditorOverlay.js @@ -8,16 +8,17 @@ import QueryMaker from 'src/data_explorer/components/QueryMaker' import Visualization from 'src/data_explorer/components/Visualization' import OverlayControls from 'src/dashboards/components/OverlayControls' import DisplayOptions from 'src/dashboards/components/DisplayOptions' + import * as queryModifiers from 'src/utils/queryTransitions' import defaultQueryConfig from 'src/utils/defaultQueryConfig' import buildInfluxQLQuery from 'utils/influxql' import {getQueryConfig} from 'shared/apis' +import {buildYLabel} from 'shared/presenters' import {removeUnselectedTemplateValues} from 'src/dashboards/constants' import {OVERLAY_TECHNOLOGY} from 'shared/constants/classNames' import {MINIMUM_HEIGHTS, INITIAL_HEIGHTS} from 'src/data_explorer/constants' -import {buildYLabel} from 'shared/presenters' class CellEditorOverlay extends Component { constructor(props) { diff --git a/ui/src/dashboards/components/GraphTypeSelector.js b/ui/src/dashboards/components/GraphTypeSelector.js index 92ff5313ee..ee23f444a2 100644 --- a/ui/src/dashboards/components/GraphTypeSelector.js +++ b/ui/src/dashboards/components/GraphTypeSelector.js @@ -1,5 +1,6 @@ import React, {PropTypes} from 'react' import classnames from 'classnames' + import {graphTypes} from 'src/dashboards/graphics/graph' const GraphTypeSelector = ({selectedGraphType, onSelectGraphType}) => From bfd4ca8a5bab67501105e2935bbb382bec12de79 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 3 Aug 2017 09:40:24 -0700 Subject: [PATCH 81/93] Test all things --- ui/spec/shared/parsing/getRangeForDygraphSpec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/spec/shared/parsing/getRangeForDygraphSpec.js b/ui/spec/shared/parsing/getRangeForDygraphSpec.js index d7840444f3..c44d1a4a9a 100644 --- a/ui/spec/shared/parsing/getRangeForDygraphSpec.js +++ b/ui/spec/shared/parsing/getRangeForDygraphSpec.js @@ -6,7 +6,7 @@ const mid = 10 const min = 5 const kapacitor = {value: null, rangeValue: null, operator: null} -describe.only('getRangeForDygraphSpec', () => { +describe('getRangeForDygraphSpec', () => { it('gets the range for one timeSeries', () => { const timeSeries = [[date, min], [date, mid], [date, max]] const actual = getRange(timeSeries) From fba53666b9cf7eae95587c2c3a743abd3e01ba48 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 3 Aug 2017 12:16:26 -0700 Subject: [PATCH 82/93] Add user instructions for how to use display options --- ui/src/dashboards/components/Ranger.js | 3 +++ ui/src/shared/parsing/getRangeForDygraph.js | 7 +++++-- ui/src/style/components/ceo-display-options.scss | 14 ++++++++++++-- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/ui/src/dashboards/components/Ranger.js b/ui/src/dashboards/components/Ranger.js index bad00dfba6..4ab4c0ea32 100644 --- a/ui/src/dashboards/components/Ranger.js +++ b/ui/src/dashboards/components/Ranger.js @@ -47,6 +47,9 @@ const Ranger = ({onSetRange, onSetLabel, axes}) => { placeholder="auto" />
    +

    + Values left blank will be set automatically +

    {/*
    Date: Thu, 3 Aug 2017 13:43:46 -0700 Subject: [PATCH 83/93] Rename ranger to AxesOptions --- ui/src/dashboards/components/{Ranger.js => AxesOptions.js} | 6 +++--- ui/src/dashboards/components/DisplayOptions.js | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) rename ui/src/dashboards/components/{Ranger.js => AxesOptions.js} (96%) diff --git a/ui/src/dashboards/components/Ranger.js b/ui/src/dashboards/components/AxesOptions.js similarity index 96% rename from ui/src/dashboards/components/Ranger.js rename to ui/src/dashboards/components/AxesOptions.js index 4ab4c0ea32..d8ae22d597 100644 --- a/ui/src/dashboards/components/Ranger.js +++ b/ui/src/dashboards/components/AxesOptions.js @@ -2,7 +2,7 @@ import React, {PropTypes} from 'react' import _ from 'lodash' // TODO: add logic for for Prefix, Suffix, Scale, and Multiplier -const Ranger = ({onSetRange, onSetLabel, axes}) => { +const AxesOptions = ({onSetRange, onSetLabel, axes}) => { const min = _.get(axes, ['y', 'bounds', '0'], '') const max = _.get(axes, ['y', 'bounds', '1'], '') const label = _.get(axes, ['y', 'label'], '') @@ -89,7 +89,7 @@ const Ranger = ({onSetRange, onSetLabel, axes}) => { const {arrayOf, func, shape, string} = PropTypes -Ranger.propTypes = { +AxesOptions.propTypes = { onSetRange: func.isRequired, onSetLabel: func.isRequired, axes: shape({ @@ -100,4 +100,4 @@ Ranger.propTypes = { }).isRequired, } -export default Ranger +export default AxesOptions diff --git a/ui/src/dashboards/components/DisplayOptions.js b/ui/src/dashboards/components/DisplayOptions.js index 8d9e57e82f..693ad4da78 100644 --- a/ui/src/dashboards/components/DisplayOptions.js +++ b/ui/src/dashboards/components/DisplayOptions.js @@ -1,7 +1,7 @@ import React, {PropTypes} from 'react' import GraphTypeSelector from 'src/dashboards/components/GraphTypeSelector' -import Ranger from 'src/dashboards/components/Ranger' +import AxesOptions from 'src/dashboards/components/AxesOptions' const DisplayOptions = ({ selectedGraphType, @@ -15,7 +15,7 @@ const DisplayOptions = ({ selectedGraphType={selectedGraphType} onSelectGraphType={onSelectGraphType} /> - +
    const {func, shape, string} = PropTypes From 47b5f6cf4dae9a93668b47dd4435a05056516fa5 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 3 Aug 2017 14:28:58 -0700 Subject: [PATCH 84/93] Add logic to handle ranges that are submitted as equal --- ui/spec/shared/parsing/getRangeForDygraphSpec.js | 8 ++++++++ ui/src/shared/parsing/getRangeForDygraph.js | 15 ++++++++------- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/ui/spec/shared/parsing/getRangeForDygraphSpec.js b/ui/spec/shared/parsing/getRangeForDygraphSpec.js index c44d1a4a9a..1c3c7d80ef 100644 --- a/ui/spec/shared/parsing/getRangeForDygraphSpec.js +++ b/ui/spec/shared/parsing/getRangeForDygraphSpec.js @@ -23,6 +23,14 @@ describe('getRangeForDygraphSpec', () => { expect(actual).to.deep.equal([0, 4]) }) + it('does not use the user submitted range if they are equal', () => { + const timeSeries = [[date, min], [date, max], [date, mid]] + const providedRange = ['0', '0'] + const actual = getRange(timeSeries, providedRange) + + expect(actual).to.deep.equal([min, max]) + }) + it('gets the range for multiple timeSeries', () => { const timeSeries = [[date, null, min], [date, max, mid], [date, null, mid]] const actual = getRange(timeSeries) diff --git a/ui/src/shared/parsing/getRangeForDygraph.js b/ui/src/shared/parsing/getRangeForDygraph.js index bb874b3588..7a19fa439f 100644 --- a/ui/src/shared/parsing/getRangeForDygraph.js +++ b/ui/src/shared/parsing/getRangeForDygraph.js @@ -1,6 +1,6 @@ const PADDING_FACTOR = 0.1 -const considerZero = (userNumber, number) => { +const considerEmpty = (userNumber, number) => { if (userNumber === '') { return null } @@ -20,10 +20,6 @@ const getRange = ( const {value, rangeValue, operator} = ruleValues const [userMin, userMax] = userSelectedRange - if (userMin && userMax) { - return [considerZero(userMin), considerZero(userMax)] - } - const subtractPadding = val => +val - Math.abs(val * PADDING_FACTOR) const addPadding = val => +val + Math.abs(val * PADDING_FACTOR) @@ -65,8 +61,9 @@ const getRange = ( [null, null] ) - // If time series is such that min and max are equal use Dygraph defaults const [min, max] = range + + // If time series is such that min and max are equal use Dygraph defaults if (min === max) { return [null, null] } @@ -75,7 +72,11 @@ const getRange = ( return [min, max] } - return [considerZero(userMin, min), considerZero(userMax, max)] + if (userMin && userMax) { + return [considerEmpty(userMin), considerEmpty(userMax)] + } + + return [considerEmpty(userMin, min), considerEmpty(userMax, max)] } export default getRange From e11d2d38aac28299ae5db2aa67288b3d8bf48068 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 3 Aug 2017 14:50:44 -0700 Subject: [PATCH 85/93] Pass queryConfig down to Dygraphs so it can make label decisions --- ui/src/data_explorer/components/Visualization.js | 5 +++-- ui/src/shared/components/Dygraph.js | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ui/src/data_explorer/components/Visualization.js b/ui/src/data_explorer/components/Visualization.js index 7466ff69ac..b33b99a7b3 100644 --- a/ui/src/data_explorer/components/Visualization.js +++ b/ui/src/data_explorer/components/Visualization.js @@ -102,10 +102,11 @@ const Visualization = React.createClass({ const statements = queryConfigs.map(query => { const text = query.rawText || buildInfluxQLQuery(query.range || timeRange, query) - return {text, id: query.id} + return {text, id: query.id, queryConfig: query} }) + const queries = statements.filter(s => s.text !== null).map(s => { - return {host: [proxy], text: s.text, id: s.id} + return {host: [proxy], text: s.text, id: s.id, queryConfig: s.queryConfig} }) return ( diff --git a/ui/src/shared/components/Dygraph.js b/ui/src/shared/components/Dygraph.js index 0ddf8bf74f..697a2ae880 100644 --- a/ui/src/shared/components/Dygraph.js +++ b/ui/src/shared/components/Dygraph.js @@ -271,11 +271,12 @@ export default class Dygraph extends Component { const y = _.get(axes, ['y', 'bounds'], [null, null]) const y2 = _.get(axes, ['y2', 'bounds'], undefined) const timeSeries = this.getTimeSeries() + const ylabel = this.getLabel('y') const updateOptions = { labels, file: timeSeries, - ylabel: this.getLabel('y'), + ylabel, axes: { y: { valueRange: getRange(timeSeries, y, ruleValues), From 5cd915c19c4bb2120244ae6e2b0168f4c0ad151b Mon Sep 17 00:00:00 2001 From: Jared Scheib Date: Thu, 3 Aug 2017 14:50:41 -0700 Subject: [PATCH 86/93] Use UUID for AlertsTable element keys to prevent overlap --- ui/src/alerts/components/AlertsTable.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ui/src/alerts/components/AlertsTable.js b/ui/src/alerts/components/AlertsTable.js index f5dfce8932..fba3e21095 100644 --- a/ui/src/alerts/components/AlertsTable.js +++ b/ui/src/alerts/components/AlertsTable.js @@ -1,7 +1,9 @@ import React, {Component, PropTypes} from 'react' + import _ from 'lodash' import classnames from 'classnames' import {Link} from 'react-router' +import uuid from 'node-uuid' import FancyScrollbar from 'shared/components/FancyScrollbar' @@ -130,10 +132,7 @@ class AlertsTable extends Component { > {alerts.map(({name, level, time, host, value}) => { return ( -
    +
    Date: Thu, 3 Aug 2017 15:03:47 -0700 Subject: [PATCH 87/93] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 97ea199c47..f0650e4ef9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ### Bug Fixes 1. [#1798](https://github.com/influxdata/chronograf/pull/1798): Fix domain not updating in visualizations when changing time range manually 1. [#1799](https://github.com/influxdata/chronograf/pull/1799): Prevent console error spam from Dygraph.synchronize when a dashboard has only one graph +1. [#1813](https://github.com/influxdata/chronograf/pull/1813): Guarantee UUID for each Alert Table key to prevent dropping items when keys overlap ### Features ### UI Improvements From f8b13dcfa39e07b654f9f76efcd451c1d2f64b4f Mon Sep 17 00:00:00 2001 From: Jared Scheib Date: Thu, 3 Aug 2017 15:14:22 -0700 Subject: [PATCH 88/93] Clarify and specify var and func names --- .../components/CellEditorOverlay.js | 23 ++++++++++--------- .../dashboards/components/OverlayControls.js | 8 +++---- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/ui/src/dashboards/components/CellEditorOverlay.js b/ui/src/dashboards/components/CellEditorOverlay.js index 2c1ae67a0d..19a5c00c35 100644 --- a/ui/src/dashboards/components/CellEditorOverlay.js +++ b/ui/src/dashboards/components/CellEditorOverlay.js @@ -29,10 +29,11 @@ class CellEditorOverlay extends Component { this.handleDeleteQuery = ::this.handleDeleteQuery this.handleSaveCell = ::this.handleSaveCell this.handleSelectGraphType = ::this.handleSelectGraphType - this.handleSelectDisplayOptions = ::this.handleSelectDisplayOptions + this.handleMakeDisplayOptionsTabActive = ::this + .handleMakeDisplayOptionsTabActive this.handleSetActiveQueryIndex = ::this.handleSetActiveQueryIndex this.handleEditRawText = ::this.handleEditRawText - this.handleSetRange = ::this.handleSetRange + this.handleSetYAxisBounds = ::this.handleSetYAxisBounds this.handleSetLabel = ::this.handleSetLabel const {cell: {name, type, queries, axes}} = props @@ -46,7 +47,7 @@ class CellEditorOverlay extends Component { cellWorkingType: type, queriesWorkingDraft, activeQueryIndex: 0, - isDisplayOptionsTabOpen: false, + isDisplayOptionsTabActive: false, axes: this.setDefaultLabels(axes, queries), } } @@ -93,7 +94,7 @@ class CellEditorOverlay extends Component { } } - handleSetRange(e) { + handleSetYAxisBounds(e) { const {min, max} = e.target.form const {axes} = this.state @@ -157,9 +158,9 @@ class CellEditorOverlay extends Component { this.setState({cellWorkingType: graphType}) } - handleSelectDisplayOptions(isDisplayOptionsTabOpen) { + handleMakeDisplayOptionsTabActive(isDisplayOptionsTabActive) { return () => { - this.setState({isDisplayOptionsTabOpen}) + this.setState({isDisplayOptionsTabActive}) } } @@ -197,7 +198,7 @@ class CellEditorOverlay extends Component { activeQueryIndex, cellWorkingName, cellWorkingType, - isDisplayOptionsTabOpen, + isDisplayOptionsTabActive, queriesWorkingDraft, axes, } = this.state @@ -235,17 +236,17 @@ class CellEditorOverlay extends Component { />
    - {isDisplayOptionsTabOpen + {isDisplayOptionsTabActive ? diff --git a/ui/src/dashboards/components/OverlayControls.js b/ui/src/dashboards/components/OverlayControls.js index 7eb510c1fa..15bc0d53de 100644 --- a/ui/src/dashboards/components/OverlayControls.js +++ b/ui/src/dashboards/components/OverlayControls.js @@ -6,7 +6,7 @@ import ConfirmButtons from 'shared/components/ConfirmButtons' const OverlayControls = ({ onCancel, onSave, - isDisplayOptionsTabOpen, + isDisplayOptionsTabActive, onSelectDisplayOptions, isSavable, }) => @@ -16,7 +16,7 @@ const OverlayControls = ({
  • @@ -25,7 +25,7 @@ const OverlayControls = ({
  • @@ -46,7 +46,7 @@ const {func, bool} = PropTypes OverlayControls.propTypes = { onCancel: func.isRequired, onSave: func.isRequired, - isDisplayOptionsTabOpen: bool.isRequired, + isDisplayOptionsTabActive: bool.isRequired, onSelectDisplayOptions: func.isRequired, isSavable: bool, } From e6ffc0e25683bfbccf5a0ff3c504a01c7dee7879 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 3 Aug 2017 17:22:35 -0700 Subject: [PATCH 89/93] Update function names for fun and profit --- ui/src/dashboards/components/CellEditorOverlay.js | 7 +++---- ui/src/dashboards/components/OverlayControls.js | 8 ++++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/ui/src/dashboards/components/CellEditorOverlay.js b/ui/src/dashboards/components/CellEditorOverlay.js index 19a5c00c35..57ce0d3492 100644 --- a/ui/src/dashboards/components/CellEditorOverlay.js +++ b/ui/src/dashboards/components/CellEditorOverlay.js @@ -29,8 +29,7 @@ class CellEditorOverlay extends Component { this.handleDeleteQuery = ::this.handleDeleteQuery this.handleSaveCell = ::this.handleSaveCell this.handleSelectGraphType = ::this.handleSelectGraphType - this.handleMakeDisplayOptionsTabActive = ::this - .handleMakeDisplayOptionsTabActive + this.handleClickDisplayOptionsTab = ::this.handleClickDisplayOptionsTab this.handleSetActiveQueryIndex = ::this.handleSetActiveQueryIndex this.handleEditRawText = ::this.handleEditRawText this.handleSetYAxisBounds = ::this.handleSetYAxisBounds @@ -158,7 +157,7 @@ class CellEditorOverlay extends Component { this.setState({cellWorkingType: graphType}) } - handleMakeDisplayOptionsTabActive(isDisplayOptionsTabActive) { + handleClickDisplayOptionsTab(isDisplayOptionsTabActive) { return () => { this.setState({isDisplayOptionsTabActive}) } @@ -237,7 +236,7 @@ class CellEditorOverlay extends Component {
    @@ -18,7 +18,7 @@ const OverlayControls = ({ className={classnames({ active: !isDisplayOptionsTabActive, })} - onClick={onSelectDisplayOptions(false)} + onClick={onClickDisplayOptions(false)} > Queries
  • @@ -27,7 +27,7 @@ const OverlayControls = ({ className={classnames({ active: isDisplayOptionsTabActive, })} - onClick={onSelectDisplayOptions(true)} + onClick={onClickDisplayOptions(true)} > Display Options @@ -47,7 +47,7 @@ OverlayControls.propTypes = { onCancel: func.isRequired, onSave: func.isRequired, isDisplayOptionsTabActive: bool.isRequired, - onSelectDisplayOptions: func.isRequired, + onClickDisplayOptions: func.isRequired, isSavable: bool, } From c7d0c06804c2800749ffe0598f0ec36f3bae7031 Mon Sep 17 00:00:00 2001 From: Hunter Trujillo Date: Mon, 7 Aug 2017 08:31:11 -0700 Subject: [PATCH 90/93] Remove inner modulo from hasherino. --- ui/src/shared/components/Dygraph.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/shared/components/Dygraph.js b/ui/src/shared/components/Dygraph.js index ccd73ef39f..d47d8226aa 100644 --- a/ui/src/shared/components/Dygraph.js +++ b/ui/src/shared/components/Dygraph.js @@ -14,7 +14,7 @@ const hasherino = (str, len) => str .split('') .map(char => char.charCodeAt(0)) - .reduce((hash, code) => (hash + code) % len, 0) + .reduce((hash, code) => hash + code, 0) % len export default class Dygraph extends Component { constructor(props) { From 09274c7881b56a78197d8213decf3b9aa1f7b663 Mon Sep 17 00:00:00 2001 From: Hunter Trujillo Date: Mon, 7 Aug 2017 09:06:55 -0700 Subject: [PATCH 91/93] Add graphics comments. Fix redundant math. --- ui/src/shared/graphs/helpers.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ui/src/shared/graphs/helpers.js b/ui/src/shared/graphs/helpers.js index 2470fafc5d..33786c8b5e 100644 --- a/ui/src/shared/graphs/helpers.js +++ b/ui/src/shared/graphs/helpers.js @@ -51,7 +51,9 @@ export const multiColumnBarPlotter = e => { } } - const barWidth = Math.max(Math.floor(2.0 / 3.0 * minSep), 1) + // calculate bar width using some graphics math while + // ensuring a bar is never smaller than one px, so it is always rendered + const barWidth = Math.max(Math.floor(2.0 / 3.0 * minSep), 1.0) const fillColors = [] const strokeColors = g.getColors() @@ -75,8 +77,8 @@ export const multiColumnBarPlotter = e => { ctx.fillStyle = fillColors[j] const xLeft = sets.length === 1 - ? centerX - barWidth / 1 - : centerX - barWidth / 1 * (1 - j / sets.length) + ? centerX - barWidth + : centerX - barWidth * (1 - j / sets.length) ctx.fillRect( xLeft, @@ -85,6 +87,7 @@ export const multiColumnBarPlotter = e => { yBottom - p.canvasy ) + // hover highlighting if (selPointX === centerX) { ctx.strokeRect( xLeft, From 76d3962e6a8de1c684d16f3e4192374f8c8527ac Mon Sep 17 00:00:00 2001 From: Hunter Trujillo Date: Mon, 7 Aug 2017 09:12:14 -0700 Subject: [PATCH 92/93] update changelog update --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eca666284d..3265649d8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,8 @@ ### UI Improvements 1. [#1796](https://github.com/influxdata/chronograf/pull/1796): Add spinner to indicate data is being written 1. [#1800](https://github.com/influxdata/chronograf/pull/1796): Embiggen text area for line protocol manual entry in Data Explorer's Write Data overlay -1. [#1805](https://github.com/influxdata/chronograf/pull/1805): Bar graphs no longer overlap with each other, and bonus, series names are hashed so that graph colors should stay the same for the same series across charts +1. [#1805](https://github.com/influxdata/chronograf/pull/1805): Fix bar graphs overlapping +1. [#1805](https://github.com/influxdata/chronograf/pull/1805): Add series names hashing so that graph colors should stay the same for the same series across charts ## v1.3.5.0 [2017-07-27] ### Bug Fixes From 3482246b9682cafc3c09ba171d51770cc2a2a10b Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Mon, 7 Aug 2017 12:56:22 -0700 Subject: [PATCH 93/93] Add hashing logic to componentDidUpdate If we dont run the hashing logic during component update series added after component mount wont necessarily line up with their hashed colors. --- ui/src/shared/components/Dygraph.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/ui/src/shared/components/Dygraph.js b/ui/src/shared/components/Dygraph.js index 165fad10cf..e90fa28fd1 100644 --- a/ui/src/shared/components/Dygraph.js +++ b/ui/src/shared/components/Dygraph.js @@ -272,6 +272,7 @@ export default class Dygraph extends Component { dygraphSeries, ruleValues, isBarGraph, + overrideLineColors, } = this.props const dygraph = this.dygraph @@ -285,6 +286,17 @@ export default class Dygraph extends Component { const y2 = _.get(axes, ['y2', 'bounds'], undefined) const timeSeries = this.getTimeSeries() const ylabel = this.getLabel('y') + const finalLineColors = [...(overrideLineColors || LINE_COLORS)] + + const hashColorDygraphSeries = {} + const {length} = finalLineColors + + for (const seriesName in dygraphSeries) { + const series = dygraphSeries[seriesName] + const hashIndex = hasherino(seriesName, length) + const color = finalLineColors[hashIndex] + hashColorDygraphSeries[seriesName] = {...series, color} + } const updateOptions = { labels, @@ -301,7 +313,8 @@ export default class Dygraph extends Component { stepPlot: options.stepPlot, stackedGraph: options.stackedGraph, underlayCallback: options.underlayCallback, - series: dygraphSeries, + colors: finalLineColors, + series: hashColorDygraphSeries, plotter: isBarGraph ? multiColumnBarPlotter : null, visibility: this.visibility(), }