diff --git a/ui/src/dashboards/actions/cellEditorOverlay.js b/ui/src/dashboards/actions/cellEditorOverlay.js
index 34b4a5354e..f9efe0de08 100644
--- a/ui/src/dashboards/actions/cellEditorOverlay.js
+++ b/ui/src/dashboards/actions/cellEditorOverlay.js
@@ -57,3 +57,10 @@ export const updateTableOptions = tableOptions => ({
tableOptions,
},
})
+
+export const updateLineColors = lineColors => ({
+ type: 'UPDATE_LINE_COLORS',
+ payload: {
+ lineColors,
+ },
+})
diff --git a/ui/src/dashboards/components/AxesOptions.js b/ui/src/dashboards/components/AxesOptions.js
index c0e5019a7d..9b04de4659 100644
--- a/ui/src/dashboards/components/AxesOptions.js
+++ b/ui/src/dashboards/components/AxesOptions.js
@@ -7,6 +7,7 @@ import OptIn from 'shared/components/OptIn'
import Input from 'src/dashboards/components/DisplayOptionsInput'
import {Tabber, Tab} from 'src/dashboards/components/Tabber'
import FancyScrollbar from 'shared/components/FancyScrollbar'
+import LineGraphColorSelector from 'src/shared/components/LineGraphColorSelector'
import {
AXES_SCALE_OPTIONS,
@@ -102,6 +103,7 @@ class AxesOptions extends Component {
type="text"
/>
+
{
const {queriesWorkingDraft, staticLegend} = this.state
- const {cell, thresholdsListColors, gaugeColors} = this.props
+ const {cell, thresholdsListColors, gaugeColors, lineColors} = this.props
const queries = queriesWorkingDraft.map(q => {
const timeRange = q.range || {upper: null, lower: ':dashboardTime:'}
@@ -133,6 +133,12 @@ class CellEditorOverlay extends Component {
colors = stringifyColorValues(thresholdsListColors)
break
}
+ case 'bar':
+ case 'line':
+ case 'line-stacked':
+ case 'line-stepplot': {
+ colors = stringifyColorValues(lineColors)
+ }
}
this.props.onSave({
@@ -392,6 +398,7 @@ CellEditorOverlay.propTypes = {
thresholdsListType: string.isRequired,
thresholdsListColors: arrayOf(shape({}).isRequired).isRequired,
gaugeColors: arrayOf(shape({}).isRequired).isRequired,
+ lineColors: arrayOf(shape({}).isRequired).isRequired,
}
CEOBottom.propTypes = {
diff --git a/ui/src/dashboards/components/Visualization.js b/ui/src/dashboards/components/Visualization.js
index d84a76ed06..b9131af830 100644
--- a/ui/src/dashboards/components/Visualization.js
+++ b/ui/src/dashboards/components/Visualization.js
@@ -14,6 +14,7 @@ const DashVisualization = (
type,
templates,
timeRange,
+ lineColors,
autoRefresh,
gaugeColors,
queryConfigs,
@@ -26,14 +27,32 @@ const DashVisualization = (
},
{source: {links: {proxy}}}
) => {
- const colors = type === 'gauge' ? gaugeColors : thresholdsListColors
+ let colors = []
+ switch (type) {
+ case 'gauge': {
+ colors = stringifyColorValues(gaugeColors)
+ break
+ }
+ case 'single-stat':
+ case 'line-plus-single-stat':
+ case 'table': {
+ colors = stringifyColorValues(thresholdsListColors)
+ break
+ }
+ case 'bar':
+ case 'line':
+ case 'line-stacked':
+ case 'line-stepplot': {
+ colors = stringifyColorValues(lineColors)
+ }
+ }
return (
({
gaugeColors,
thresholdsListColors,
+ lineColors,
type,
axes,
tableOptions,
diff --git a/ui/src/dashboards/containers/DashboardPage.js b/ui/src/dashboards/containers/DashboardPage.js
index 29516e202d..5df566e8ab 100644
--- a/ui/src/dashboards/containers/DashboardPage.js
+++ b/ui/src/dashboards/containers/DashboardPage.js
@@ -267,6 +267,7 @@ class DashboardPage extends Component {
showTemplateControlBar,
dashboard,
dashboards,
+ lineColors,
gaugeColors,
autoRefresh,
selectedCell,
@@ -383,6 +384,7 @@ class DashboardPage extends Component {
thresholdsListType={thresholdsListType}
thresholdsListColors={thresholdsListColors}
gaugeColors={gaugeColors}
+ lineColors={lineColors}
/>
) : null}
{
@@ -532,6 +535,7 @@ const mapStateToProps = (state, {params: {dashboardID}}) => {
thresholdsListType,
thresholdsListColors,
gaugeColors,
+ lineColors,
},
} = state
@@ -562,6 +566,7 @@ const mapStateToProps = (state, {params: {dashboardID}}) => {
thresholdsListType,
thresholdsListColors,
gaugeColors,
+ lineColors,
}
}
diff --git a/ui/src/dashboards/reducers/cellEditorOverlay.js b/ui/src/dashboards/reducers/cellEditorOverlay.js
index 4629e89750..d51d2c503c 100644
--- a/ui/src/dashboards/reducers/cellEditorOverlay.js
+++ b/ui/src/dashboards/reducers/cellEditorOverlay.js
@@ -9,6 +9,11 @@ import {
getThresholdsListType,
} from 'shared/constants/thresholds'
+import {
+ DEFAULT_LINE_COLORS,
+ validateLineColors,
+} from 'src/shared/constants/graphColorPalettes'
+
import {initializeOptions} from 'src/dashboards/constants/cellEditor'
export const initialState = {
@@ -16,6 +21,7 @@ export const initialState = {
thresholdsListType: THRESHOLD_TYPE_TEXT,
thresholdsListColors: DEFAULT_THRESHOLDS_LIST_COLORS,
gaugeColors: DEFAULT_GAUGE_COLORS,
+ lineColors: DEFAULT_LINE_COLORS,
}
export default function cellEditorOverlay(state = initialState, action) {
@@ -36,12 +42,15 @@ export default function cellEditorOverlay(state = initialState, action) {
initializeOptions('table')
)
+ const lineColors = validateLineColors(colors)
+
return {
...state,
cell: {...cell, tableOptions},
thresholdsListType,
thresholdsListColors,
gaugeColors,
+ lineColors,
}
}
@@ -101,6 +110,12 @@ export default function cellEditorOverlay(state = initialState, action) {
return {...state, cell}
}
+
+ case 'UPDATE_LINE_COLORS': {
+ const {lineColors} = action.payload
+
+ return {...state, lineColors}
+ }
}
return state
diff --git a/ui/src/shared/components/Dygraph.js b/ui/src/shared/components/Dygraph.js
index 7fe6e70399..e5a166093d 100644
--- a/ui/src/shared/components/Dygraph.js
+++ b/ui/src/shared/components/Dygraph.js
@@ -4,6 +4,7 @@ import PropTypes from 'prop-types'
import {connect} from 'react-redux'
import shallowCompare from 'react-addons-shallow-compare'
import _ from 'lodash'
+import chroma from 'chroma-js'
import NanoDate from 'nano-date'
import Dygraphs from 'src/external/dygraph'
@@ -26,7 +27,11 @@ import {
highlightSeriesOpts,
} from 'src/shared/graphs/helpers'
-import {generateColorScale} from 'src/shared/constants/graphColorPalettes'
+import {
+ DEFAULT_LINE_COLORS,
+ validateLineColors,
+ transformColorsForChroma,
+} from 'src/shared/constants/graphColorPalettes'
const {LINEAR, LOG, BASE_10, BASE_2} = AXES_SCALE_OPTIONS
class Dygraph extends Component {
@@ -191,15 +196,35 @@ class Dygraph extends Component {
}
colorDygraphSeries = () => {
- const {dygraphSeries} = this.props
+ const {dygraphSeries, children, colors, overrideLineColors} = this.props
const numSeries = Object.keys(dygraphSeries).length
- const colors = generateColorScale(numSeries)
+ const validatedLineColors = validateLineColors(colors) // ensures safe defaults
+
+ let lineColors = chroma
+ .scale(transformColorsForChroma(validatedLineColors))
+ .mode('lch')
+ .colors(numSeries)
+
+ if (React.children && React.children.count(children)) {
+ // If graph is line-plus-single-stat then reserve colors for single stat
+ lineColors = chroma
+ .scale(transformColorsForChroma(DEFAULT_LINE_COLORS))
+ .mode('lch')
+ .colors(numSeries)
+ }
+
+ if (overrideLineColors && overrideLineColors.length > 0) {
+ lineColors = chroma
+ .scale(overrideLineColors)
+ .mode('lch')
+ .colors(numSeries)
+ }
const coloredDygraphSeries = {}
for (const seriesName in dygraphSeries) {
const series = dygraphSeries[seriesName]
- const color = colors[Object.keys(dygraphSeries).indexOf(seriesName)]
+ const color = lineColors[Object.keys(dygraphSeries).indexOf(seriesName)]
coloredDygraphSeries[seriesName] = {...series, color}
}
@@ -423,7 +448,7 @@ Dygraph.propTypes = {
isGraphFilled: bool,
isBarGraph: bool,
staticLegend: bool,
- overrideLineColors: array,
+ overrideLineColors: arrayOf(string.isRequired),
dygraphSeries: shape({}).isRequired,
ruleValues: shape({
operator: string,
@@ -440,6 +465,15 @@ Dygraph.propTypes = {
onZoom: func,
mode: string,
children: node,
+ colors: arrayOf(
+ shape({
+ type: string.isRequired,
+ hex: string.isRequired,
+ id: string.isRequired,
+ name: string.isRequired,
+ value: string.isRequired,
+ }).isRequired
+ ),
}
const mapStateToProps = ({annotations: {mode}}) => ({
diff --git a/ui/src/shared/components/LineGraph.js b/ui/src/shared/components/LineGraph.js
index a7cabea3a1..305602b525 100644
--- a/ui/src/shared/components/LineGraph.js
+++ b/ui/src/shared/components/LineGraph.js
@@ -6,8 +6,6 @@ import shallowCompare from 'react-addons-shallow-compare'
import SingleStat from 'src/shared/components/SingleStat'
import timeSeriesToDygraph from 'utils/timeSeriesToDygraph'
-import {SINGLE_STAT_LINE_COLORS} from 'src/shared/graphs/helpers'
-
class LineGraph extends Component {
constructor(props) {
super(props)
@@ -84,10 +82,6 @@ class LineGraph extends Component {
connectSeparatedPoints: true,
}
- const lineColors = showSingleStat
- ? SINGLE_STAT_LINE_COLORS
- : overrideLineColors
-
const containerStyle = {
width: 'calc(100% - 32px)',
height: 'calc(100% - 16px)',
@@ -117,7 +111,8 @@ class LineGraph extends Component {
resizeCoords={resizeCoords}
dygraphSeries={dygraphSeries}
setResolution={setResolution}
- overrideLineColors={lineColors}
+ colors={colors}
+ overrideLineColors={overrideLineColors}
containerStyle={containerStyle}
staticLegend={staticLegend}
isGraphFilled={showSingleStat ? false : isGraphFilled}
diff --git a/ui/src/shared/components/LineGraphColorSelector.js b/ui/src/shared/components/LineGraphColorSelector.js
new file mode 100644
index 0000000000..a8bfc8e06b
--- /dev/null
+++ b/ui/src/shared/components/LineGraphColorSelector.js
@@ -0,0 +1,59 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import {connect} from 'react-redux'
+import {bindActionCreators} from 'redux'
+
+import ColorScaleDropdown from 'shared/components/ColorScaleDropdown'
+
+import {updateLineColors} from 'src/dashboards/actions/cellEditorOverlay'
+
+class LineGraphColorSelector extends Component {
+ handleSelectColors = colorScale => {
+ const {handleUpdateLineColors} = this.props
+ const {colors} = colorScale
+
+ handleUpdateLineColors(colors)
+ }
+
+ render() {
+ const {lineColors} = this.props
+
+ return (
+
+
+
+
+ )
+ }
+}
+
+const {arrayOf, func, shape, string, number} = PropTypes
+
+LineGraphColorSelector.propTypes = {
+ lineColors: arrayOf(
+ shape({
+ type: string.isRequired,
+ hex: string.isRequired,
+ id: string.isRequired,
+ name: string.isRequired,
+ value: number.isRequired,
+ }).isRequired
+ ).isRequired,
+ handleUpdateLineColors: func.isRequired,
+}
+
+const mapStateToProps = ({cellEditorOverlay: {lineColors}}) => ({
+ lineColors,
+})
+
+const mapDispatchToProps = dispatch => ({
+ handleUpdateLineColors: bindActionCreators(updateLineColors, dispatch),
+})
+
+export default connect(mapStateToProps, mapDispatchToProps)(
+ LineGraphColorSelector
+)
diff --git a/ui/src/shared/constants/graphColorPalettes.js b/ui/src/shared/constants/graphColorPalettes.js
index ba8773c829..c7d6ed9f5c 100644
--- a/ui/src/shared/constants/graphColorPalettes.js
+++ b/ui/src/shared/constants/graphColorPalettes.js
@@ -1,24 +1,23 @@
-import _ from 'lodash'
-import chroma from 'chroma-js'
+const COLOR_TYPE_SCALE = 'scale'
// Color Palettes
export const LINE_COLORS_A = [
{
- type: 'scale',
+ type: COLOR_TYPE_SCALE,
hex: '#31C0F6',
id: '0',
name: 'Nineteen Eighty Four',
value: 0,
},
{
- type: 'scale',
+ type: COLOR_TYPE_SCALE,
hex: '#A500A5',
id: '0',
name: 'Nineteen Eighty Four',
value: 0,
},
{
- type: 'scale',
+ type: COLOR_TYPE_SCALE,
hex: '#FF7E27',
id: '0',
name: 'Nineteen Eighty Four',
@@ -28,21 +27,21 @@ export const LINE_COLORS_A = [
export const LINE_COLORS_B = [
{
- type: 'scale',
+ type: COLOR_TYPE_SCALE,
hex: '#74D495',
id: '1',
name: 'Atlantis',
value: 0,
},
{
- type: 'scale',
+ type: COLOR_TYPE_SCALE,
hex: '#3F3FBA',
id: '1',
name: 'Atlantis',
value: 0,
},
{
- type: 'scale',
+ type: COLOR_TYPE_SCALE,
hex: '#EA5994',
id: '1',
name: 'Atlantis',
@@ -52,124 +51,126 @@ export const LINE_COLORS_B = [
export const LINE_COLORS_C = [
{
- type: 'scale',
+ type: COLOR_TYPE_SCALE,
hex: '#8F8AF4',
id: '1',
- name: 'Glarbh',
+ name: 'Do Androids Dream of Electric Sheep?',
value: 0,
},
{
- type: 'scale',
+ type: COLOR_TYPE_SCALE,
hex: '#A51414',
id: '1',
- name: 'Glarbh',
+ name: 'Do Androids Dream of Electric Sheep?',
value: 0,
},
{
- type: 'scale',
+ type: COLOR_TYPE_SCALE,
hex: '#F4CF31',
id: '1',
- name: 'Glarbh',
+ name: 'Do Androids Dream of Electric Sheep?',
value: 0,
},
]
export const LINE_COLORS_D = [
{
- type: 'scale',
+ type: COLOR_TYPE_SCALE,
hex: '#FD7A5D',
id: '1',
- name: 'Spoot',
+ name: 'Delorean',
value: 0,
},
{
- type: 'scale',
+ type: COLOR_TYPE_SCALE,
hex: '#5F1CF2',
id: '1',
- name: 'Spoot',
+ name: 'Delorean',
value: 0,
},
{
- type: 'scale',
+ type: COLOR_TYPE_SCALE,
hex: '#4CE09A',
id: '1',
- name: 'Spoot',
+ name: 'Delorean',
value: 0,
},
]
export const LINE_COLORS_E = [
{
- type: 'scale',
+ type: COLOR_TYPE_SCALE,
hex: '#FDC44F',
id: '1',
- name: 'Swump',
+ name: 'Cthulhu',
value: 0,
},
{
- type: 'scale',
+ type: COLOR_TYPE_SCALE,
hex: '#007C76',
id: '1',
- name: 'Swump',
+ name: 'Cthulhu',
value: 0,
},
{
- type: 'scale',
+ type: COLOR_TYPE_SCALE,
hex: '#8983FF',
id: '1',
- name: 'Swump',
+ name: 'Cthulhu',
value: 0,
},
]
export const LINE_COLORS_F = [
{
- type: 'scale',
+ type: COLOR_TYPE_SCALE,
hex: '#DA6FF1',
id: '1',
- name: 'Splort',
+ name: 'Ectoplasm',
value: 0,
},
{
- type: 'scale',
+ type: COLOR_TYPE_SCALE,
hex: '#00717A',
id: '1',
- name: 'Splort',
+ name: 'Ectoplasm',
value: 0,
},
{
- type: 'scale',
+ type: COLOR_TYPE_SCALE,
hex: '#05B7E0',
id: '1',
- name: 'Splort',
+ name: 'Ectoplasm',
value: 0,
},
]
export const LINE_COLORS_G = [
{
- type: 'scale',
+ type: COLOR_TYPE_SCALE,
hex: '#F6F6F8',
id: '1',
- name: 'OldTimey',
+ name: 'T-Max 400 Film',
value: 0,
},
{
- type: 'scale',
+ type: COLOR_TYPE_SCALE,
hex: '#A4A8B6',
id: '1',
- name: 'OldTimey',
+ name: 'T-Max 400 Film',
value: 0,
},
{
- type: 'scale',
+ type: COLOR_TYPE_SCALE,
hex: '#545667',
id: '1',
- name: 'OldTimey',
+ name: 'T-Max 400 Film',
value: 0,
},
]
+export const DEFAULT_LINE_COLORS = LINE_COLORS_A
+
export const LINE_COLOR_SCALES = [
LINE_COLORS_A,
LINE_COLORS_B,
@@ -180,23 +181,24 @@ export const LINE_COLOR_SCALES = [
LINE_COLORS_G,
].map(colorScale => {
const name = colorScale[0].name
- const colors = colorScale.map(color => color.hex)
+ const colors = colorScale
const id = colorScale[0].id
return {name, colors, id}
})
-const paletteA = ['#31C0F6', '#A500A5', '#FF7E27']
-const paletteB = ['#74D495', '#3F3FBA', '#EA5994']
-const paletteC = ['#8F8AF4', '#A51414', '#F4CF31']
-const paletteD = ['#FD7A5D', '#5F1CF2', '#4CE09A']
-const paletteE = ['#FDC44F', '#007C76', '#8983FF']
-const paletteF = ['#DA6FF1', '#00717A', '#05B7E0']
-const paletteG = ['#F6F6F8', '#A4A8B6', '#545667']
-
-export const generateColorScale = numSeries => {
- return chroma
- .scale(paletteB)
- .mode('lch')
- .colors(numSeries)
+export const transformColorsForChroma = colors => {
+ return colors.map(color => color.hex)
+}
+
+export const validateLineColors = colors => {
+ if (!colors || colors.length !== 3) {
+ return DEFAULT_LINE_COLORS
+ }
+
+ const testColorsTypes =
+ colors.filter(color => color.type === COLOR_TYPE_SCALE).length ===
+ colors.length
+
+ return testColorsTypes ? colors : DEFAULT_LINE_COLORS
}