From 1b5f8ebd6d94527114be11b866495d6e61a313b6 Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 28 Feb 2018 14:22:45 -0800 Subject: [PATCH 01/21] WIP color series based on quantity --- ui/src/shared/components/Dygraph.js | 22 ++++---- ui/src/shared/constants/graphColorPalettes.js | 53 +++++++++++++++++++ 2 files changed, 65 insertions(+), 10 deletions(-) create mode 100644 ui/src/shared/constants/graphColorPalettes.js diff --git a/ui/src/shared/components/Dygraph.js b/ui/src/shared/components/Dygraph.js index f30d888267..b7bf17418f 100644 --- a/ui/src/shared/components/Dygraph.js +++ b/ui/src/shared/components/Dygraph.js @@ -21,9 +21,9 @@ import { LABEL_WIDTH, CHAR_PIXELS, barPlotter, - hasherino, highlightSeriesOpts, } from 'src/shared/graphs/helpers' +import {getIdealColors} from 'src/shared/constants/graphColorPalettes' const {LINEAR, LOG, BASE_10, BASE_2} = DISPLAY_OPTIONS class Dygraph extends Component { @@ -51,7 +51,7 @@ class Dygraph extends Component { fillGraph, logscale: y.scale === LOG, colors: this.getLineColors(), - series: this.hashColorDygraphSeries(), + series: this.colorDygraphSeries(), unhighlightCallback: this.unhighlightCallback, plugins: [new Dygraphs.Plugins.Crosshair({direction: 'vertical'})], axes: { @@ -153,7 +153,7 @@ class Dygraph extends Component { }, }, colors: this.getLineColors(), - series: this.hashColorDygraphSeries(), + series: this.colorDygraphSeries(), plotter: isBarGraph ? barPlotter : null, drawCallback: this.annotationsRef.heartbeat, } @@ -193,19 +193,21 @@ class Dygraph extends Component { onZoom(this.formatTimeRange(lower), this.formatTimeRange(upper)) } - hashColorDygraphSeries = () => { + colorDygraphSeries = () => { const {dygraphSeries} = this.props - const colors = this.getLineColors() - const hashColorDygraphSeries = {} + const numSeries = Object.keys(dygraphSeries).length + const colors = getIdealColors(numSeries) + + const coloredDygraphSeries = {} for (const seriesName in dygraphSeries) { const series = dygraphSeries[seriesName] - const hashIndex = hasherino(seriesName, colors.length) - const color = colors[hashIndex] - hashColorDygraphSeries[seriesName] = {...series, color} + const color = colors[Object.keys(dygraphSeries).indexOf(seriesName)] + + coloredDygraphSeries[seriesName] = {...series, color} } - return hashColorDygraphSeries + return coloredDygraphSeries } sync = () => { diff --git a/ui/src/shared/constants/graphColorPalettes.js b/ui/src/shared/constants/graphColorPalettes.js new file mode 100644 index 0000000000..c5a3e27656 --- /dev/null +++ b/ui/src/shared/constants/graphColorPalettes.js @@ -0,0 +1,53 @@ +// Tier 5 Colors +const series1 = ['#22ADF6'] // Blue +const series2 = [...series1, '#4ED8A0'] // Green +const series3 = [...series2, '#7A65F2'] // Purple +const series4 = [...series3, '#F95F53'] // Red +const series5 = [...series4, '#FFB94A'] // Yellow +// Tier 4 Colors +const series6 = [...series5, '#00C9FF'] // Blu +const series7 = [...series6, '#7CE490'] // Green +const series8 = [...series7, '#9394FF'] // Purple +const series9 = [...series8, '#FF8564'] // Red +const series10 = [...series9, '#FFD255'] // Yellow +// Tier 6 Colors +const series11 = [...series10, '#4591ED'] // Blu +const series12 = [...series11, '#32B08C'] // Green +const series13 = [...series12, '#513CC6'] // Purple +const series14 = [...series13, '#DC4E58'] // Red +const series15 = [...series14, '#F48D38'] // Yellow +// Tier 3 Colors +const series16 = [...series15, '#6BDFFF'] // Blu +const series17 = [...series16, '#A5F3B4'] // Green +const series18 = [...series17, '#B1B6FF'] // Purple +const series19 = [...series18, '#FFB6A0'] // Red +const series20 = [...series19, '#FFE480'] // Yellow + +// All Colors +const graphColors = [ + series1, + series2, + series3, + series4, + series5, + series6, + series7, + series8, + series9, + series10, + series11, + series12, + series13, + series14, + series15, + series16, + series17, + series18, + series19, + series20, +] + +// Color Finder +export const getIdealColors = numSeries => { + return graphColors[numSeries - 1] +} From c74adab81673cff02368f46e3e4ce004d36676fc Mon Sep 17 00:00:00 2001 From: Alex P Date: Mon, 26 Mar 2018 13:14:42 -0700 Subject: [PATCH 02/21] Prototype HSL shifting color spectrum --- ui/src/shared/constants/colorOperations.js | 45 ++++++++++++++++++- ui/src/shared/constants/graphColorPalettes.js | 41 +++++++++++++++-- 2 files changed, 82 insertions(+), 4 deletions(-) diff --git a/ui/src/shared/constants/colorOperations.js b/ui/src/shared/constants/colorOperations.js index e96291e528..6747a22001 100644 --- a/ui/src/shared/constants/colorOperations.js +++ b/ui/src/shared/constants/colorOperations.js @@ -121,7 +121,7 @@ export const generateThresholdsListHexs = ( return {bgColor, textColor} } -// Handy tool for converting hexcodes to HSV values +// Handy tool for converting Hexcodes to HSL values export const HexcodeToHSL = hex => { const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex) @@ -160,3 +160,46 @@ export const HexcodeToHSL = hex => { return {hue, saturation, lightness} } + +// Handy tool for converting HSL values to Hexcode +export const HSLToHexcode = (hue, saturation, lightness) => { + hue /= 360 + saturation /= 100 + lightness /= 100 + let red, green, blue + if (saturation === 0) { + red = green = blue = lightness // achromatic + } else { + const hue2rgb = (p, q, t) => { + if (t < 0) { + t += 1 + } + if (t > 1) { + t -= 1 + } + if (t < 1 / 6) { + return p + (q - p) * 6 * t + } + if (t < 1 / 2) { + return q + } + if (t < 2 / 3) { + return p + (q - p) * (2 / 3 - t) * 6 + } + return p + } + const q = + lightness < 0.5 + ? lightness * (1 + saturation) + : lightness + saturation - lightness * saturation + const p = 2 * lightness - q + red = hue2rgb(p, q, hue + 1 / 3) + green = hue2rgb(p, q, hue) + blue = hue2rgb(p, q, hue - 1 / 3) + } + const toHex = x => { + const hex = Math.round(x * 255).toString(16) + return hex.length === 1 ? `0${hex}` : hex + } + return `#${toHex(red)}${toHex(green)}${toHex(blue)}` +} diff --git a/ui/src/shared/constants/graphColorPalettes.js b/ui/src/shared/constants/graphColorPalettes.js index 2f08559b56..c1198bd73e 100644 --- a/ui/src/shared/constants/graphColorPalettes.js +++ b/ui/src/shared/constants/graphColorPalettes.js @@ -1,4 +1,4 @@ -import {HexcodeToHSL} from 'src/shared/constants/colorOperations' +import {HexcodeToHSL, HSLToHexcode} from 'src/shared/constants/colorOperations' import _ from 'lodash' // Tier 5 Colors @@ -50,18 +50,53 @@ const graphColors = [ series20, ] +export const generateLargePalette = numSeries => { + const start = {hue: 190, saturation: 90, lightness: 50} + const end = {hue: 360, saturation: 80, lightness: 98} + const colorsHSL = [] + + for (let i = 0; i < numSeries; i++) { + const hRange = end.hue - start.hue + const hStep = hRange / (numSeries - 1) + const h = hStep * i + + const sRange = end.saturation - start.saturation + const sStep = sRange / (numSeries - 1) + const s = sStep * i + + const lRange = end.lightness - start.lightness + const lStep = lRange / (numSeries - 1) + const l = lStep * i + + colorsHSL[i] = { + hue: Math.floor(start.hue + h), + saturation: Math.floor(start.saturation + s), + lightness: Math.floor(start.lightness + l), + } + } + + const colorsHex = colorsHSL.map(color => + HSLToHexcode(color.hue, color.saturation, color.lightness) + ) + + return colorsHex +} + // Sort by hue const sortColorsByHue = colors => { return _.sortBy(colors, color => { const {hue} = HexcodeToHSL(color) - console.log(color, hue) + return hue }) } // Color Finder export const getIdealColors = numSeries => { + if (numSeries > 18) { + return generateLargePalette(numSeries) + } const colors = graphColors[numSeries - 1] - console.log(sortColorsByHue(colors)) + return sortColorsByHue(colors) } From 853a3c0d6e7335ec9214823fd47a0f05c3faf93b Mon Sep 17 00:00:00 2001 From: Alex P Date: Mon, 26 Mar 2018 16:23:46 -0700 Subject: [PATCH 03/21] Add Chroma.js Package --- ui/package.json | 3 ++- ui/yarn.lock | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ui/package.json b/ui/package.json index 6b95613217..9b6d82d3f0 100644 --- a/ui/package.json +++ b/ui/package.json @@ -116,6 +116,7 @@ "axios": "^0.13.1", "bignumber.js": "^4.0.2", "calculate-size": "^1.1.1", + "chroma-js": "^1.3.6", "classnames": "^2.2.3", "dygraphs": "2.1.0", "eslint-plugin-babel": "^4.1.2", @@ -147,4 +148,4 @@ "rome": "^2.1.22", "uuid": "^3.2.1" } -} \ No newline at end of file +} diff --git a/ui/yarn.lock b/ui/yarn.lock index 0c053dee44..b8c44de637 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -1686,6 +1686,10 @@ chownr@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181" +chroma-js@^1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/chroma-js/-/chroma-js-1.3.6.tgz#22dd7220ef6b55dcfcb8ef92982baaf55dced45d" + ci-info@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.1.2.tgz#03561259db48d0474c8bdc90f5b47b068b6bbfb4" From a28a30d45cfc87825aa9bfb6f36b4b7a47ffb741 Mon Sep 17 00:00:00 2001 From: Alex P Date: Mon, 26 Mar 2018 16:24:15 -0700 Subject: [PATCH 04/21] Refactor color operations to use Chroma --- ui/src/shared/constants/colorOperations.js | 27 +++++----------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/ui/src/shared/constants/colorOperations.js b/ui/src/shared/constants/colorOperations.js index 2a4dedf3a4..0219ae1d29 100644 --- a/ui/src/shared/constants/colorOperations.js +++ b/ui/src/shared/constants/colorOperations.js @@ -1,36 +1,21 @@ import _ from 'lodash' +import chroma from 'chroma-js' + import { THRESHOLD_COLORS, THRESHOLD_TYPE_BASE, THRESHOLD_TYPE_TEXT, } from 'shared/constants/thresholds' -const hexToRgb = hex => { - const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex) - return result - ? { - r: parseInt(result[1], 16), - g: parseInt(result[2], 16), - b: parseInt(result[3], 16), - } - : null -} - -const averageRgbValues = valuesObject => { - const {r, g, b} = valuesObject - return (r + g + b) / 3 -} - -const trueNeutralGrey = 128 +const luminanceThreshold = 0.5 const getLegibleTextColor = bgColorHex => { - const averageBackground = averageRgbValues(hexToRgb(bgColorHex)) - const isBackgroundLight = averageBackground > trueNeutralGrey - const darkText = '#292933' const lightText = '#ffffff' - return isBackgroundLight ? darkText : lightText + return chroma(bgColorHex).luminance() < luminanceThreshold + ? darkText + : lightText } const findNearestCrossedThreshold = (colors, lastValue) => { From 635b0b91cfd79a2799208d01633bb3f40176936d Mon Sep 17 00:00:00 2001 From: Alex P Date: Mon, 26 Mar 2018 16:55:46 -0700 Subject: [PATCH 05/21] WIP Introduce a handful of color presets --- ui/src/shared/components/Dygraph.js | 4 +- ui/src/shared/constants/graphColorPalettes.js | 282 ++++++++++++------ 2 files changed, 193 insertions(+), 93 deletions(-) diff --git a/ui/src/shared/components/Dygraph.js b/ui/src/shared/components/Dygraph.js index a835bc9ab1..7fe6e70399 100644 --- a/ui/src/shared/components/Dygraph.js +++ b/ui/src/shared/components/Dygraph.js @@ -26,7 +26,7 @@ import { highlightSeriesOpts, } from 'src/shared/graphs/helpers' -import {getIdealColors} from 'src/shared/constants/graphColorPalettes' +import {generateColorScale} from 'src/shared/constants/graphColorPalettes' const {LINEAR, LOG, BASE_10, BASE_2} = AXES_SCALE_OPTIONS class Dygraph extends Component { @@ -193,7 +193,7 @@ class Dygraph extends Component { colorDygraphSeries = () => { const {dygraphSeries} = this.props const numSeries = Object.keys(dygraphSeries).length - const colors = getIdealColors(numSeries) + const colors = generateColorScale(numSeries) const coloredDygraphSeries = {} diff --git a/ui/src/shared/constants/graphColorPalettes.js b/ui/src/shared/constants/graphColorPalettes.js index c1198bd73e..ba8773c829 100644 --- a/ui/src/shared/constants/graphColorPalettes.js +++ b/ui/src/shared/constants/graphColorPalettes.js @@ -1,102 +1,202 @@ -import {HexcodeToHSL, HSLToHexcode} from 'src/shared/constants/colorOperations' import _ from 'lodash' +import chroma from 'chroma-js' -// Tier 5 Colors -const series1 = ['#22ADF6'] // Blue -const series2 = [...series1, '#4ED8A0'] // Green -const series3 = [...series2, '#7A65F2'] // Purple -const series4 = [...series3, '#F95F53'] // Red -const series5 = [...series4, '#FFB94A'] // Yellow -// Tier 4 Colors -const series6 = [...series5, '#00C9FF'] // Blu -const series7 = [...series6, '#7CE490'] // Green -const series8 = [...series7, '#9394FF'] // Purple -const series9 = [...series8, '#FF8564'] // Red -const series10 = [...series9, '#FFD255'] // Yellow -// Tier 6 Colors -const series11 = [...series10, '#4591ED'] // Blu -const series12 = [...series11, '#32B08C'] // Green -const series13 = [...series12, '#513CC6'] // Purple -const series14 = [...series13, '#DB4D4D'] // Red -const series15 = [...series14, '#F48D38'] // Yellow -// Tier 3 Colors -const series16 = [...series15, '#6BDFFF'] // Blu -const series17 = [...series16, '#A5F3B4'] // Green -const series18 = [...series17, '#B1B6FF'] // Purple -const series19 = [...series18, '#FFB6A0'] // Red -const series20 = [...series19, '#FFE480'] // Yellow - -// All Colors -const graphColors = [ - series1, - series2, - series3, - series4, - series5, - series6, - series7, - series8, - series9, - series10, - series11, - series12, - series13, - series14, - series15, - series16, - series17, - series18, - series19, - series20, +// Color Palettes +export const LINE_COLORS_A = [ + { + type: 'scale', + hex: '#31C0F6', + id: '0', + name: 'Nineteen Eighty Four', + value: 0, + }, + { + type: 'scale', + hex: '#A500A5', + id: '0', + name: 'Nineteen Eighty Four', + value: 0, + }, + { + type: 'scale', + hex: '#FF7E27', + id: '0', + name: 'Nineteen Eighty Four', + value: 0, + }, ] -export const generateLargePalette = numSeries => { - const start = {hue: 190, saturation: 90, lightness: 50} - const end = {hue: 360, saturation: 80, lightness: 98} - const colorsHSL = [] +export const LINE_COLORS_B = [ + { + type: 'scale', + hex: '#74D495', + id: '1', + name: 'Atlantis', + value: 0, + }, + { + type: 'scale', + hex: '#3F3FBA', + id: '1', + name: 'Atlantis', + value: 0, + }, + { + type: 'scale', + hex: '#EA5994', + id: '1', + name: 'Atlantis', + value: 0, + }, +] - for (let i = 0; i < numSeries; i++) { - const hRange = end.hue - start.hue - const hStep = hRange / (numSeries - 1) - const h = hStep * i +export const LINE_COLORS_C = [ + { + type: 'scale', + hex: '#8F8AF4', + id: '1', + name: 'Glarbh', + value: 0, + }, + { + type: 'scale', + hex: '#A51414', + id: '1', + name: 'Glarbh', + value: 0, + }, + { + type: 'scale', + hex: '#F4CF31', + id: '1', + name: 'Glarbh', + value: 0, + }, +] - const sRange = end.saturation - start.saturation - const sStep = sRange / (numSeries - 1) - const s = sStep * i +export const LINE_COLORS_D = [ + { + type: 'scale', + hex: '#FD7A5D', + id: '1', + name: 'Spoot', + value: 0, + }, + { + type: 'scale', + hex: '#5F1CF2', + id: '1', + name: 'Spoot', + value: 0, + }, + { + type: 'scale', + hex: '#4CE09A', + id: '1', + name: 'Spoot', + value: 0, + }, +] - const lRange = end.lightness - start.lightness - const lStep = lRange / (numSeries - 1) - const l = lStep * i +export const LINE_COLORS_E = [ + { + type: 'scale', + hex: '#FDC44F', + id: '1', + name: 'Swump', + value: 0, + }, + { + type: 'scale', + hex: '#007C76', + id: '1', + name: 'Swump', + value: 0, + }, + { + type: 'scale', + hex: '#8983FF', + id: '1', + name: 'Swump', + value: 0, + }, +] - colorsHSL[i] = { - hue: Math.floor(start.hue + h), - saturation: Math.floor(start.saturation + s), - lightness: Math.floor(start.lightness + l), - } - } +export const LINE_COLORS_F = [ + { + type: 'scale', + hex: '#DA6FF1', + id: '1', + name: 'Splort', + value: 0, + }, + { + type: 'scale', + hex: '#00717A', + id: '1', + name: 'Splort', + value: 0, + }, + { + type: 'scale', + hex: '#05B7E0', + id: '1', + name: 'Splort', + value: 0, + }, +] - const colorsHex = colorsHSL.map(color => - HSLToHexcode(color.hue, color.saturation, color.lightness) - ) +export const LINE_COLORS_G = [ + { + type: 'scale', + hex: '#F6F6F8', + id: '1', + name: 'OldTimey', + value: 0, + }, + { + type: 'scale', + hex: '#A4A8B6', + id: '1', + name: 'OldTimey', + value: 0, + }, + { + type: 'scale', + hex: '#545667', + id: '1', + name: 'OldTimey', + value: 0, + }, +] - return colorsHex -} - -// Sort by hue -const sortColorsByHue = colors => { - return _.sortBy(colors, color => { - const {hue} = HexcodeToHSL(color) - - return hue - }) -} - -// Color Finder -export const getIdealColors = numSeries => { - if (numSeries > 18) { - return generateLargePalette(numSeries) - } - const colors = graphColors[numSeries - 1] - - return sortColorsByHue(colors) +export const LINE_COLOR_SCALES = [ + LINE_COLORS_A, + LINE_COLORS_B, + LINE_COLORS_C, + LINE_COLORS_D, + LINE_COLORS_E, + LINE_COLORS_F, + LINE_COLORS_G, +].map(colorScale => { + const name = colorScale[0].name + const colors = colorScale.map(color => color.hex) + 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) } From e0acfb91195573725ec3a200098b617f290b97ca Mon Sep 17 00:00:00 2001 From: Alex P Date: Mon, 26 Mar 2018 19:09:09 -0700 Subject: [PATCH 06/21] Add "Scale" type to accepted cell color types --- server/cells.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/cells.go b/server/cells.go index 181095478d..b8fabf2a45 100644 --- a/server/cells.go +++ b/server/cells.go @@ -113,7 +113,7 @@ func HasCorrectAxes(c *chronograf.DashboardCell) error { // HasCorrectColors verifies that the format of each color is correct func HasCorrectColors(c *chronograf.DashboardCell) error { for _, color := range c.CellColors { - if !oneOf(color.Type, "max", "min", "threshold", "text", "background") { + if !oneOf(color.Type, "max", "min", "threshold", "text", "background", "scale") { return chronograf.ErrInvalidColorType } if len(color.Hex) != 7 { From c1f3de244c5694db9e6693093646e328833a8cda Mon Sep 17 00:00:00 2001 From: Alex P Date: Mon, 26 Mar 2018 19:09:29 -0700 Subject: [PATCH 07/21] Introduce component for selecting color scales --- .../shared/components/ColorScaleDropdown.js | 118 ++++++++++++++++++ ui/src/style/components/color-dropdown.scss | 37 ++++-- 2 files changed, 145 insertions(+), 10 deletions(-) create mode 100644 ui/src/shared/components/ColorScaleDropdown.js diff --git a/ui/src/shared/components/ColorScaleDropdown.js b/ui/src/shared/components/ColorScaleDropdown.js new file mode 100644 index 0000000000..909d0bcc9e --- /dev/null +++ b/ui/src/shared/components/ColorScaleDropdown.js @@ -0,0 +1,118 @@ +import React, {Component} from 'react' +import PropTypes from 'prop-types' +import uuid from 'uuid' +import classnames from 'classnames' + +import OnClickOutside from 'shared/components/OnClickOutside' +import FancyScrollbar from 'shared/components/FancyScrollbar' + +import {LINE_COLOR_SCALES} from 'src/shared/constants/graphColorPalettes' + +class ColorScaleDropdown extends Component { + constructor(props) { + super(props) + + this.state = { + expanded: false, + } + } + + handleToggleMenu = () => { + const {disabled} = this.props + + if (disabled) { + return + } + this.setState({expanded: !this.state.expanded}) + } + + handleClickOutside = () => { + this.setState({expanded: false}) + } + + handleDropdownClick = colorScale => () => { + this.props.onChoose(colorScale) + this.setState({expanded: false}) + } + + generateGradientStyle = colors => ({ + background: `linear-gradient(to right, ${colors[0].hex} 0%,${ + colors[1].hex + } 50%,${colors[2].hex} 100%)`, + }) + + render() { + const {expanded} = this.state + const {selected, disabled, stretchToFit} = this.props + + const dropdownClassNames = classnames('color-dropdown', { + open: expanded, + 'color-dropdown--stretch': stretchToFit, + }) + const toggleClassNames = classnames( + 'btn btn-sm btn-default color-dropdown--toggle', + {active: expanded, 'color-dropdown__disabled': disabled} + ) + + return ( +
+
+
+
{selected[0].name}
+ +
+ {expanded ? ( +
+ + {LINE_COLOR_SCALES.map(colorScale => ( +
+
+ + {colorScale.name} + +
+ ))} + +
+ ) : null} +
+ ) + } +} + +const {arrayOf, bool, func, shape, string, number} = PropTypes + +ColorScaleDropdown.propTypes = { + selected: arrayOf( + shape({ + type: string.isRequired, + hex: string.isRequired, + id: string.isRequired, + name: string.isRequired, + value: number.isRequired, + }).isRequired + ).isRequired, + onChoose: func.isRequired, + stretchToFit: bool, + disabled: bool, +} + +export default OnClickOutside(ColorScaleDropdown) diff --git a/ui/src/style/components/color-dropdown.scss b/ui/src/style/components/color-dropdown.scss index b892cd0b22..99f6901f7b 100644 --- a/ui/src/style/components/color-dropdown.scss +++ b/ui/src/style/components/color-dropdown.scss @@ -4,6 +4,10 @@ */ $color-dropdown--circle: 14px; +$color-dropdown--bar: 104px; +$color-dropdown--bar-height: 10px; +$color-dropdown--left-padding: 11px; +$color-dropdown--name-padding: 20px; .color-dropdown { width: 140px; @@ -31,11 +35,11 @@ $color-dropdown--circle: 14px; position: absolute; top: 30px; left: 0; - z-index: 2; + z-index: 5; width: 100%; border-radius: 4px; box-shadow: 0 2px 5px 0.6px fade-out($g0-obsidian, 0.7); - @include gradient-h($g0-obsidian,$g2-kevlar); + @include gradient-h($g0-obsidian, $g2-kevlar); } .color-dropdown--item { @include no-user-select(); @@ -43,9 +47,7 @@ $color-dropdown--circle: 14px; height: 28px; position: relative; color: $g11-sidewalk; - transition: - color 0.25s ease, - background-color 0.25s ease; + transition: color 0.25s ease, background-color 0.25s ease; &:hover { background-color: $g4-onyx; @@ -67,6 +69,7 @@ $color-dropdown--circle: 14px; } } .color-dropdown--swatch, +.color-dropdown--swatches, .color-dropdown--name { position: absolute; top: 50%; @@ -76,21 +79,35 @@ $color-dropdown--circle: 14px; width: $color-dropdown--circle; height: $color-dropdown--circle; border-radius: 50%; - left: 11px; + left: $color-dropdown--left-padding; +} +.color-dropdown--swatches { + width: $color-dropdown--bar; + height: $color-dropdown--bar-height; + border-radius: $color-dropdown--bar-height / 2; + left: $color-dropdown--left-padding; } .color-dropdown--name { text-align: left; - right: 11px; - left: 34px; + right: $color-dropdown--name-padding; + left: $color-dropdown--circle + $color-dropdown--name-padding; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; font-weight: 600; text-transform: capitalize; + + .color-dropdown--swatches + & { + left: $color-dropdown--bar + $color-dropdown--name-padding; + } } -.color-dropdown .color-dropdown--menu .fancy-scroll--container .fancy-scroll--track-v .fancy-scroll--thumb-v { - @include gradient-v($g9-mountain,$g7-graphite); +.color-dropdown + .color-dropdown--menu + .fancy-scroll--container + .fancy-scroll--track-v + .fancy-scroll--thumb-v { + @include gradient-v($g9-mountain, $g7-graphite); } .color-dropdown--toggle.color-dropdown__disabled { color: $g7-graphite; From a3fc9b778cd69963cc6bb9126c25dd3f72e774d1 Mon Sep 17 00:00:00 2001 From: Alex P Date: Mon, 26 Mar 2018 19:10:19 -0700 Subject: [PATCH 08/21] Implement updating and rendering of line color scales --- .../dashboards/actions/cellEditorOverlay.js | 7 ++ ui/src/dashboards/components/AxesOptions.js | 2 + .../components/CellEditorOverlay.js | 9 +- ui/src/dashboards/components/Visualization.js | 34 +++++- ui/src/dashboards/containers/DashboardPage.js | 5 + .../dashboards/reducers/cellEditorOverlay.js | 15 +++ ui/src/shared/components/Dygraph.js | 44 +++++++- ui/src/shared/components/LineGraph.js | 9 +- .../components/LineGraphColorSelector.js | 59 ++++++++++ ui/src/shared/constants/graphColorPalettes.js | 106 +++++++++--------- 10 files changed, 223 insertions(+), 67 deletions(-) create mode 100644 ui/src/shared/components/LineGraphColorSelector.js 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 } From 6edb4f30e461fcfe0438dd765a8654f1d5664eb4 Mon Sep 17 00:00:00 2001 From: Alex P Date: Mon, 26 Mar 2018 19:14:06 -0700 Subject: [PATCH 09/21] Write reducer test for updating line graph colors --- ui/test/dashboards/reducers/cellEditorOverlay.test.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ui/test/dashboards/reducers/cellEditorOverlay.test.js b/ui/test/dashboards/reducers/cellEditorOverlay.test.js index 2d29ecf67b..eb11c734e0 100644 --- a/ui/test/dashboards/reducers/cellEditorOverlay.test.js +++ b/ui/test/dashboards/reducers/cellEditorOverlay.test.js @@ -8,6 +8,7 @@ import { updateThresholdsListColors, updateThresholdsListType, updateGaugeColors, + updateLineColors, updateAxes, } from 'src/dashboards/actions/cellEditorOverlay' import {DEFAULT_TABLE_OPTIONS} from 'src/shared/constants/tableGraph' @@ -17,6 +18,7 @@ import { validateThresholdsListColors, getThresholdsListType, } from 'shared/constants/thresholds' +import {validateLineColors} from 'src/shared/constants/graphColorPalettes' const defaultCellType = 'line' const defaultCellName = 'defaultCell' @@ -45,6 +47,7 @@ const defaultThresholdsListColors = validateThresholdsListColors( defaultThresholdsListType ) const defaultGaugeColors = validateGaugeColors(defaultCell.colors) +const defaultLineColors = validateLineColors(defaultCell.colors) describe('Dashboards.Reducers.cellEditorOverlay', () => { it('should show cell editor overlay', () => { @@ -117,4 +120,11 @@ describe('Dashboards.Reducers.cellEditorOverlay', () => { expect(actual.cell.axes).toBe(expected) }) + + it('should update the cell line graph colors', () => { + const actual = reducer(initialState, updateLineColors(defaultLineColors)) + const expected = defaultLineColors + + expect(actual.lineColors).toBe(expected) + }) }) From 7d08f0bf23d5e988977364d977016f66134bb80c Mon Sep 17 00:00:00 2001 From: Alex P Date: Mon, 26 Mar 2018 19:33:47 -0700 Subject: [PATCH 10/21] Remove dots from static legend --- ui/src/shared/components/StaticLegend.js | 4 ---- ui/src/style/components/static-legend.scss | 24 ++++++---------------- 2 files changed, 6 insertions(+), 22 deletions(-) diff --git a/ui/src/shared/components/StaticLegend.js b/ui/src/shared/components/StaticLegend.js index f79c6cf115..7de93f177a 100644 --- a/ui/src/shared/components/StaticLegend.js +++ b/ui/src/shared/components/StaticLegend.js @@ -86,10 +86,6 @@ class StaticLegend extends Component { key={uuid.v4()} onMouseDown={this.handleClick(i)} > -
{removeMeasurement(v)}
))} diff --git a/ui/src/style/components/static-legend.scss b/ui/src/style/components/static-legend.scss index a9600a11a5..252620299d 100644 --- a/ui/src/style/components/static-legend.scss +++ b/ui/src/style/components/static-legend.scss @@ -16,17 +16,9 @@ flex-wrap: wrap; max-height: 50%; overflow: auto; - @include custom-scrollbar($g3-castle,$g6-smoke); -} -.static-legend--dot { - display: inline-block; - vertical-align: middle; - margin-right: 4px; - width: 8px; - height: 8px; - border-radius: 50%; - background-color: $g20-white; + @include custom-scrollbar($g3-castle, $g6-smoke); } + .static-legend--item, .static-legend--single { height: 22px; @@ -43,8 +35,7 @@ .static-legend--item { transition: background-color 0.25s ease, color 0.25s ease; - span, - .static-legend--dot { + span { opacity: 0.8; transition: opacity 0.25s ease; } @@ -53,8 +44,7 @@ cursor: pointer; background-color: $g6-smoke; - span, - .static-legend--dot { + span { opacity: 1; } } @@ -62,16 +52,14 @@ background-color: $g1-raven; font-style: italic; - span, - .static-legend--dot { + span { opacity: 0.35; } &:hover { background-color: $g2-kevlar; - span, - .static-legend--dot { + span { opacity: 0.65; } } From 4b4425e45c575914aded426aec38b01449c3438c Mon Sep 17 00:00:00 2001 From: Alex P Date: Tue, 27 Mar 2018 11:41:13 -0700 Subject: [PATCH 11/21] Improve color selection for graphs with 1 or 2 lines --- ui/src/shared/components/Dygraph.js | 14 +++--------- ui/src/shared/constants/graphColorPalettes.js | 22 +++++++++++++++---- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/ui/src/shared/components/Dygraph.js b/ui/src/shared/components/Dygraph.js index e5a166093d..ca6c80d02a 100644 --- a/ui/src/shared/components/Dygraph.js +++ b/ui/src/shared/components/Dygraph.js @@ -29,8 +29,7 @@ import { import { DEFAULT_LINE_COLORS, - validateLineColors, - transformColorsForChroma, + getLineColorsHexes, } from 'src/shared/constants/graphColorPalettes' const {LINEAR, LOG, BASE_10, BASE_2} = AXES_SCALE_OPTIONS @@ -198,19 +197,12 @@ class Dygraph extends Component { colorDygraphSeries = () => { const {dygraphSeries, children, colors, overrideLineColors} = this.props const numSeries = Object.keys(dygraphSeries).length - const validatedLineColors = validateLineColors(colors) // ensures safe defaults - let lineColors = chroma - .scale(transformColorsForChroma(validatedLineColors)) - .mode('lch') - .colors(numSeries) + let lineColors = getLineColorsHexes(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) + lineColors = getLineColorsHexes(DEFAULT_LINE_COLORS, numSeries) } if (overrideLineColors && overrideLineColors.length > 0) { diff --git a/ui/src/shared/constants/graphColorPalettes.js b/ui/src/shared/constants/graphColorPalettes.js index c7d6ed9f5c..a6095e5476 100644 --- a/ui/src/shared/constants/graphColorPalettes.js +++ b/ui/src/shared/constants/graphColorPalettes.js @@ -1,3 +1,5 @@ +import chroma from 'chroma-js' + const COLOR_TYPE_SCALE = 'scale' // Color Palettes @@ -187,10 +189,6 @@ export const LINE_COLOR_SCALES = [ return {name, colors, id} }) -export const transformColorsForChroma = colors => { - return colors.map(color => color.hex) -} - export const validateLineColors = colors => { if (!colors || colors.length !== 3) { return DEFAULT_LINE_COLORS @@ -202,3 +200,19 @@ export const validateLineColors = colors => { return testColorsTypes ? colors : DEFAULT_LINE_COLORS } + +export const getLineColorsHexes = (colors, numSeries) => { + const validatedColors = validateLineColors(colors) // ensures safe defaults + const colorsHexArray = validatedColors.map(color => color.hex) + + if (numSeries === 1) { + return [colorsHexArray[0]] + } + if (numSeries === 2) { + return [colorsHexArray[0], colorsHexArray[1]] + } + return chroma + .scale(colorsHexArray) + .mode('lch') + .colors(numSeries) +} From 763ce1b353325a23972e1693550691847dddcb3f Mon Sep 17 00:00:00 2001 From: Alex P Date: Tue, 27 Mar 2018 12:03:03 -0700 Subject: [PATCH 12/21] Remove obsolete code --- ui/src/shared/constants/colorOperations.js | 83 ---------------------- 1 file changed, 83 deletions(-) diff --git a/ui/src/shared/constants/colorOperations.js b/ui/src/shared/constants/colorOperations.js index 0219ae1d29..3c745c904f 100644 --- a/ui/src/shared/constants/colorOperations.js +++ b/ui/src/shared/constants/colorOperations.js @@ -108,86 +108,3 @@ export const generateThresholdsListHexs = ({ const textColor = baseColor.hex return {bgColor, textColor} } - -// Handy tool for converting Hexcodes to HSL values -export const HexcodeToHSL = hex => { - const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex) - - const red = parseInt(result[1], 16) / 255 - const green = parseInt(result[2], 16) / 255 - const blue = parseInt(result[3], 16) / 255 - const max = Math.max(red, green, blue) - const min = Math.min(red, green, blue) - - let hue, - saturation, - lightness = (max + min) / 2 - - if (max === min) { - hue = saturation = 0 // achromatic - } else { - const d = max - min - saturation = lightness > 0.5 ? d / (2 - max - min) : d / (max + min) - switch (max) { - case red: - hue = (green - blue) / d + (green < blue ? 6 : 0) - break - case green: - hue = (blue - red) / d + 2 - break - case blue: - hue = (red - green) / d + 4 - break - } - hue /= 6 - } - - hue = Math.round(360 * hue) - saturation = Math.round(saturation * 100) - lightness = Math.round(lightness * 100) - - return {hue, saturation, lightness} -} - -// Handy tool for converting HSL values to Hexcode -export const HSLToHexcode = (hue, saturation, lightness) => { - hue /= 360 - saturation /= 100 - lightness /= 100 - let red, green, blue - if (saturation === 0) { - red = green = blue = lightness // achromatic - } else { - const hue2rgb = (p, q, t) => { - if (t < 0) { - t += 1 - } - if (t > 1) { - t -= 1 - } - if (t < 1 / 6) { - return p + (q - p) * 6 * t - } - if (t < 1 / 2) { - return q - } - if (t < 2 / 3) { - return p + (q - p) * (2 / 3 - t) * 6 - } - return p - } - const q = - lightness < 0.5 - ? lightness * (1 + saturation) - : lightness + saturation - lightness * saturation - const p = 2 * lightness - q - red = hue2rgb(p, q, hue + 1 / 3) - green = hue2rgb(p, q, hue) - blue = hue2rgb(p, q, hue - 1 / 3) - } - const toHex = x => { - const hex = Math.round(x * 255).toString(16) - return hex.length === 1 ? `0${hex}` : hex - } - return `#${toHex(red)}${toHex(green)}${toHex(blue)}` -} From cf30c773ea2dd6067763a301bdcb9ecd79364fd2 Mon Sep 17 00:00:00 2001 From: Alex P Date: Tue, 27 Mar 2018 12:05:12 -0700 Subject: [PATCH 13/21] Improve contrast of color palette --- ui/src/shared/constants/graphColorPalettes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/shared/constants/graphColorPalettes.js b/ui/src/shared/constants/graphColorPalettes.js index a6095e5476..133ccc4bc3 100644 --- a/ui/src/shared/constants/graphColorPalettes.js +++ b/ui/src/shared/constants/graphColorPalettes.js @@ -140,7 +140,7 @@ export const LINE_COLORS_F = [ }, { type: COLOR_TYPE_SCALE, - hex: '#05B7E0', + hex: '#ACFF76', id: '1', name: 'Ectoplasm', value: 0, From c32c0eb24ace208685a85550a0fb023b6d3ccc9e Mon Sep 17 00:00:00 2001 From: Alex P Date: Tue, 27 Mar 2018 12:15:45 -0700 Subject: [PATCH 14/21] Silence console error --- ui/src/dashboards/components/Visualization.js | 30 ++----------------- .../shared/components/ColorScaleDropdown.js | 3 +- .../components/LineGraphColorSelector.js | 3 +- 3 files changed, 5 insertions(+), 31 deletions(-) diff --git a/ui/src/dashboards/components/Visualization.js b/ui/src/dashboards/components/Visualization.js index b9131af830..70da958277 100644 --- a/ui/src/dashboards/components/Visualization.js +++ b/ui/src/dashboards/components/Visualization.js @@ -88,33 +88,9 @@ DashVisualization.propTypes = { }), tableOptions: shape({}), resizerTopHeight: number, - thresholdsListColors: arrayOf( - shape({ - type: string.isRequired, - hex: string.isRequired, - id: string.isRequired, - name: string.isRequired, - value: number.isRequired, - }).isRequired - ), - gaugeColors: arrayOf( - shape({ - type: string.isRequired, - hex: string.isRequired, - id: string.isRequired, - name: string.isRequired, - value: number.isRequired, - }).isRequired - ), - lineColors: arrayOf( - shape({ - type: string.isRequired, - hex: string.isRequired, - id: string.isRequired, - name: string.isRequired, - value: number.isRequired, - }).isRequired - ), + thresholdsListColors: arrayOf(shape({}).isRequired), + gaugeColors: arrayOf(shape({}).isRequired), + lineColors: arrayOf(shape({}).isRequired), staticLegend: bool, setDataLabels: func, } diff --git a/ui/src/shared/components/ColorScaleDropdown.js b/ui/src/shared/components/ColorScaleDropdown.js index 909d0bcc9e..66ca74e55d 100644 --- a/ui/src/shared/components/ColorScaleDropdown.js +++ b/ui/src/shared/components/ColorScaleDropdown.js @@ -98,7 +98,7 @@ class ColorScaleDropdown extends Component { } } -const {arrayOf, bool, func, shape, string, number} = PropTypes +const {arrayOf, bool, func, shape, string} = PropTypes ColorScaleDropdown.propTypes = { selected: arrayOf( @@ -107,7 +107,6 @@ ColorScaleDropdown.propTypes = { hex: string.isRequired, id: string.isRequired, name: string.isRequired, - value: number.isRequired, }).isRequired ).isRequired, onChoose: func.isRequired, diff --git a/ui/src/shared/components/LineGraphColorSelector.js b/ui/src/shared/components/LineGraphColorSelector.js index a8bfc8e06b..1b197c0987 100644 --- a/ui/src/shared/components/LineGraphColorSelector.js +++ b/ui/src/shared/components/LineGraphColorSelector.js @@ -31,7 +31,7 @@ class LineGraphColorSelector extends Component { } } -const {arrayOf, func, shape, string, number} = PropTypes +const {arrayOf, func, shape, string} = PropTypes LineGraphColorSelector.propTypes = { lineColors: arrayOf( @@ -40,7 +40,6 @@ LineGraphColorSelector.propTypes = { hex: string.isRequired, id: string.isRequired, name: string.isRequired, - value: number.isRequired, }).isRequired ).isRequired, handleUpdateLineColors: func.isRequired, From 7fab9b3f6498c772a0baa42eaffd0dbb47c0a4d9 Mon Sep 17 00:00:00 2001 From: Alex P Date: Tue, 27 Mar 2018 12:19:56 -0700 Subject: [PATCH 15/21] Improve color palette contrast --- ui/src/shared/constants/graphColorPalettes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/shared/constants/graphColorPalettes.js b/ui/src/shared/constants/graphColorPalettes.js index 133ccc4bc3..ea98f269ed 100644 --- a/ui/src/shared/constants/graphColorPalettes.js +++ b/ui/src/shared/constants/graphColorPalettes.js @@ -44,7 +44,7 @@ export const LINE_COLORS_B = [ }, { type: COLOR_TYPE_SCALE, - hex: '#EA5994', + hex: '#FF4D9E', id: '1', name: 'Atlantis', value: 0, From 5d3be94f04f68f6ce5a5c820d60cbbbaf9689a2e Mon Sep 17 00:00:00 2001 From: Alex P Date: Tue, 27 Mar 2018 15:33:06 -0700 Subject: [PATCH 16/21] Make RuleGraph comply with new shape of overrideColors --- ui/src/kapacitor/components/RuleGraph.js | 5 +++-- ui/src/shared/components/Dygraph.js | 15 ++++++++------- ui/src/shared/constants/graphColorPalettes.js | 14 ++++++++++++-- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/ui/src/kapacitor/components/RuleGraph.js b/ui/src/kapacitor/components/RuleGraph.js index a181243aea..585af02b62 100644 --- a/ui/src/kapacitor/components/RuleGraph.js +++ b/ui/src/kapacitor/components/RuleGraph.js @@ -8,6 +8,8 @@ import underlayCallback from 'src/kapacitor/helpers/ruleGraphUnderlay' const RefreshingLineGraph = AutoRefresh(LineGraph) +import {LINE_COLORS_RULE_GRAPH} from 'src/shared/constants/graphColorPalettes' + const {shape, string, func} = PropTypes const RuleGraph = ({ query, @@ -20,7 +22,6 @@ const RuleGraph = ({ const autoRefreshMs = 30000 const queryText = buildInfluxQLQuery({lower}, query) const queries = [{host: source.links.proxy, text: queryText}] - const kapacitorLineColors = ['#4ED8A0'] if (!queryText) { return ( @@ -47,7 +48,7 @@ const RuleGraph = ({ isGraphFilled={false} ruleValues={rule.values} autoRefresh={autoRefreshMs} - overrideLineColors={kapacitorLineColors} + overrideLineColors={LINE_COLORS_RULE_GRAPH} underlayCallback={underlayCallback(rule)} />
diff --git a/ui/src/shared/components/Dygraph.js b/ui/src/shared/components/Dygraph.js index ca6c80d02a..5b975170be 100644 --- a/ui/src/shared/components/Dygraph.js +++ b/ui/src/shared/components/Dygraph.js @@ -4,7 +4,6 @@ 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' @@ -205,11 +204,8 @@ class Dygraph extends Component { lineColors = getLineColorsHexes(DEFAULT_LINE_COLORS, numSeries) } - if (overrideLineColors && overrideLineColors.length > 0) { - lineColors = chroma - .scale(overrideLineColors) - .mode('lch') - .colors(numSeries) + if (overrideLineColors) { + lineColors = getLineColorsHexes(overrideLineColors, numSeries) } const coloredDygraphSeries = {} @@ -440,7 +436,12 @@ Dygraph.propTypes = { isGraphFilled: bool, isBarGraph: bool, staticLegend: bool, - overrideLineColors: arrayOf(string.isRequired), + overrideLineColors: arrayOf( + shape({ + type: string.isRequired, + hex: string.isRequired, + }).isRequired + ), dygraphSeries: shape({}).isRequired, ruleValues: shape({ operator: string, diff --git a/ui/src/shared/constants/graphColorPalettes.js b/ui/src/shared/constants/graphColorPalettes.js index ea98f269ed..ce6b21a03b 100644 --- a/ui/src/shared/constants/graphColorPalettes.js +++ b/ui/src/shared/constants/graphColorPalettes.js @@ -171,6 +171,16 @@ export const LINE_COLORS_G = [ }, ] +export const LINE_COLORS_RULE_GRAPH = [ + { + type: COLOR_TYPE_SCALE, + hex: '#4ED8A0', + id: '1', + name: 'Rainforest', + value: 0, + }, +] + export const DEFAULT_LINE_COLORS = LINE_COLORS_A export const LINE_COLOR_SCALES = [ @@ -190,7 +200,7 @@ export const LINE_COLOR_SCALES = [ }) export const validateLineColors = colors => { - if (!colors || colors.length !== 3) { + if (!colors) { return DEFAULT_LINE_COLORS } @@ -205,7 +215,7 @@ export const getLineColorsHexes = (colors, numSeries) => { const validatedColors = validateLineColors(colors) // ensures safe defaults const colorsHexArray = validatedColors.map(color => color.hex) - if (numSeries === 1) { + if (numSeries === 1 || numSeries === 0) { return [colorsHexArray[0]] } if (numSeries === 2) { From 01463226293d10da21c00d6b1fac16e1ffd490bc Mon Sep 17 00:00:00 2001 From: Alex P Date: Tue, 27 Mar 2018 15:36:05 -0700 Subject: [PATCH 17/21] Updoot log of change --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b3063f7d8c..146b1505aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ 1. [#2973](https://github.com/influxdata/chronograf/pull/2973): Add unsafe SSL to Kapacitor UI configuration 1. [#3047](https://github.com/influxdata/chronograf/pull/3047): Add server flag to grant SuperAdmin status to users authenticating from a specific Auth0 Organization +1. [#3060](https://github.com/influxdata/chronograf/pull/3060): Add ability to set a color palette for Line, Stacked, Step-Plot, and Bar graphs ### UI Improvements From a65b482419f72393238981beac2957ff9e15b7e4 Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 28 Mar 2018 10:04:33 -0700 Subject: [PATCH 18/21] Guard against colors being passed in as an empty array --- ui/src/shared/constants/graphColorPalettes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/shared/constants/graphColorPalettes.js b/ui/src/shared/constants/graphColorPalettes.js index ce6b21a03b..079f45c023 100644 --- a/ui/src/shared/constants/graphColorPalettes.js +++ b/ui/src/shared/constants/graphColorPalettes.js @@ -200,7 +200,7 @@ export const LINE_COLOR_SCALES = [ }) export const validateLineColors = colors => { - if (!colors) { + if (!colors || colors.length === 0) { return DEFAULT_LINE_COLORS } From 2abe8db506889f123b10165be54f7a07929cea07 Mon Sep 17 00:00:00 2001 From: Alex P Date: Thu, 29 Mar 2018 10:27:06 -0700 Subject: [PATCH 19/21] Move changelog entry to 1.5 --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fedf7df23..331884c021 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,12 @@ ## v1.5.0.0 [unreleased] ### Features + 1. [#2526](https://github.com/influxdata/chronograf/pull/2526): Add support for RS256/JWKS verification, support for id_token parsing (as in ADFS) +1. [#3060](https://github.com/influxdata/chronograf/pull/3060): Add ability to set a color palette for Line, Stacked, Step-Plot, and Bar graphs ### UI Improvements + ### Bug Fixes ## v1.4.3.0 [unreleased] @@ -18,7 +21,6 @@ 1. [#2973](https://github.com/influxdata/chronograf/pull/2973): Add unsafe SSL to Kapacitor UI configuration 1. [#3047](https://github.com/influxdata/chronograf/pull/3047): Add server flag to grant SuperAdmin status to users authenticating from a specific Auth0 Organization -1. [#3060](https://github.com/influxdata/chronograf/pull/3060): Add ability to set a color palette for Line, Stacked, Step-Plot, and Bar graphs ### UI Improvements @@ -55,6 +57,7 @@ ## v1.4.2.1 [2018-02-28] ### Features + 1. [#2837](https://github.com/influxdata/chronograf/pull/2837): Prevent execution of queries in cells that are not in view on the dashboard page 1. [#2829](https://github.com/influxdata/chronograf/pull/2829): Add an optional persistent legend which can toggle series visibility to dashboard cells 1. [#2846](https://github.com/influxdata/chronograf/pull/2846): Allow user to annotate graphs via UI or API From b43d8a553be4185f52eb14d91eca157d586ca134 Mon Sep 17 00:00:00 2001 From: Alex P Date: Mon, 2 Apr 2018 10:51:36 -0700 Subject: [PATCH 20/21] Extract color formatting function into constant --- .../components/CellEditorOverlay.js | 28 +++++------------ ui/src/dashboards/components/Visualization.js | 27 +++++----------- ui/src/dashboards/constants/cellEditor.js | 31 +++++++++++++++++++ 3 files changed, 45 insertions(+), 41 deletions(-) diff --git a/ui/src/dashboards/components/CellEditorOverlay.js b/ui/src/dashboards/components/CellEditorOverlay.js index 44eb2e977d..7df14d5ae9 100644 --- a/ui/src/dashboards/components/CellEditorOverlay.js +++ b/ui/src/dashboards/components/CellEditorOverlay.js @@ -24,7 +24,7 @@ import { import {OVERLAY_TECHNOLOGY} from 'src/shared/constants/classNames' import {MINIMUM_HEIGHTS, INITIAL_HEIGHTS} from 'src/data_explorer/constants' import {AUTO_GROUP_BY} from 'src/shared/constants' -import {stringifyColorValues} from 'src/shared/constants/colorOperations' +import {getCellTypeColors} from 'src/dashboards/constants/cellEditor' class CellEditorOverlay extends Component { constructor(props) { @@ -120,26 +120,12 @@ class CellEditorOverlay extends Component { } }) - let colors = [] - - switch (cell.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) - } - } + const colors = getCellTypeColors({ + cellType: cell.type, + gaugeColors, + thresholdsListColors, + lineColors, + }) this.props.onSave({ ...cell, diff --git a/ui/src/dashboards/components/Visualization.js b/ui/src/dashboards/components/Visualization.js index 70da958277..1ac3d5d196 100644 --- a/ui/src/dashboards/components/Visualization.js +++ b/ui/src/dashboards/components/Visualization.js @@ -6,7 +6,7 @@ import RefreshingGraph from 'src/shared/components/RefreshingGraph' import buildQueries from 'utils/buildQueriesForGraphs' import VisualizationName from 'src/dashboards/components/VisualizationName' -import {stringifyColorValues} from 'src/shared/constants/colorOperations' +import {getCellTypeColors} from 'src/dashboards/constants/cellEditor' const DashVisualization = ( { @@ -27,25 +27,12 @@ const DashVisualization = ( }, {source: {links: {proxy}}} ) => { - 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) - } - } + const colors = getCellTypeColors({ + cellType: type, + gaugeColors, + thresholdsListColors, + lineColors, + }) return (
diff --git a/ui/src/dashboards/constants/cellEditor.js b/ui/src/dashboards/constants/cellEditor.js index f22f51f935..7ce2754c11 100644 --- a/ui/src/dashboards/constants/cellEditor.js +++ b/ui/src/dashboards/constants/cellEditor.js @@ -1,4 +1,5 @@ import {DEFAULT_TABLE_OPTIONS} from 'src/shared/constants/tableGraph' +import {stringifyColorValues} from 'src/shared/constants/colorOperations' export const initializeOptions = cellType => { switch (cellType) { @@ -18,3 +19,33 @@ export const AXES_SCALE_OPTIONS = { export const TOOLTIP_Y_VALUE_FORMAT = '

K/M/B = Thousand / Million / Billion
K/M/G = Kilo / Mega / Giga

' + +export const getCellTypeColors = ({ + cellType, + gaugeColors, + thresholdsListColors, + lineColors, +}) => { + let colors = [] + + switch (cellType) { + 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 colors +} From 5624ddc4312bae808e9ea24ccc517b994b53ad77 Mon Sep 17 00:00:00 2001 From: Alex P Date: Mon, 2 Apr 2018 11:27:35 -0700 Subject: [PATCH 21/21] Introduce and implement colors schema Had to address some errors that arose here and there from previous inconsistencies with the schema --- .../components/CellEditorOverlay.js | 7 +-- ui/src/dashboards/components/GaugeOptions.js | 13 ++---- ui/src/dashboards/components/Visualization.js | 7 +-- ui/src/dashboards/containers/DashboardPage.js | 7 +-- ui/src/data_explorer/components/VisView.js | 2 + ui/src/kapacitor/components/RuleGraph.js | 2 +- ui/src/shared/components/ColorDropdown.js | 2 +- ui/src/shared/components/Dygraph.js | 12 ++--- ui/src/shared/components/Gauge.js | 14 ++---- ui/src/shared/components/GaugeChart.js | 12 ++--- ui/src/shared/components/Layout.js | 12 ++--- ui/src/shared/components/LineGraph.js | 12 ++--- .../components/LineGraphColorSelector.js | 12 ++--- ui/src/shared/components/RefreshingGraph.js | 12 ++--- ui/src/shared/components/SingleStat.js | 11 +---- ui/src/shared/components/TableGraph.js | 11 +---- ui/src/shared/components/ThresholdsList.js | 13 ++---- ui/src/shared/constants/graphColorPalettes.js | 44 +++++++++---------- ui/src/shared/schemas.js | 22 +++++++++- ui/src/status/fixtures.js | 3 ++ 20 files changed, 94 insertions(+), 136 deletions(-) diff --git a/ui/src/dashboards/components/CellEditorOverlay.js b/ui/src/dashboards/components/CellEditorOverlay.js index 7df14d5ae9..8aa3b4fb8c 100644 --- a/ui/src/dashboards/components/CellEditorOverlay.js +++ b/ui/src/dashboards/components/CellEditorOverlay.js @@ -25,6 +25,7 @@ import {OVERLAY_TECHNOLOGY} from 'src/shared/constants/classNames' import {MINIMUM_HEIGHTS, INITIAL_HEIGHTS} from 'src/data_explorer/constants' import {AUTO_GROUP_BY} from 'src/shared/constants' import {getCellTypeColors} from 'src/dashboards/constants/cellEditor' +import {colorsStringSchema, colorsNumberSchema} from 'shared/schemas' class CellEditorOverlay extends Component { constructor(props) { @@ -382,9 +383,9 @@ CellEditorOverlay.propTypes = { dashboardID: string.isRequired, sources: arrayOf(shape()), thresholdsListType: string.isRequired, - thresholdsListColors: arrayOf(shape({}).isRequired).isRequired, - gaugeColors: arrayOf(shape({}).isRequired).isRequired, - lineColors: arrayOf(shape({}).isRequired).isRequired, + thresholdsListColors: colorsNumberSchema.isRequired, + gaugeColors: colorsNumberSchema.isRequired, + lineColors: colorsStringSchema.isRequired, } CEOBottom.propTypes = { diff --git a/ui/src/dashboards/components/GaugeOptions.js b/ui/src/dashboards/components/GaugeOptions.js index 3197464465..a4b2d4abe0 100644 --- a/ui/src/dashboards/components/GaugeOptions.js +++ b/ui/src/dashboards/components/GaugeOptions.js @@ -20,6 +20,7 @@ import { updateGaugeColors, updateAxes, } from 'src/dashboards/actions/cellEditorOverlay' +import {colorsNumberSchema} from 'shared/schemas' class GaugeOptions extends Component { handleAddThreshold = () => { @@ -219,18 +220,10 @@ class GaugeOptions extends Component { } } -const {arrayOf, func, number, shape, string} = PropTypes +const {func, shape} = PropTypes GaugeOptions.propTypes = { - gaugeColors: arrayOf( - shape({ - type: string.isRequired, - hex: string.isRequired, - id: string.isRequired, - name: string.isRequired, - value: number.isRequired, - }).isRequired - ), + gaugeColors: colorsNumberSchema, handleUpdateGaugeColors: func.isRequired, handleUpdateAxes: func.isRequired, axes: shape({}).isRequired, diff --git a/ui/src/dashboards/components/Visualization.js b/ui/src/dashboards/components/Visualization.js index 1ac3d5d196..5fe725856f 100644 --- a/ui/src/dashboards/components/Visualization.js +++ b/ui/src/dashboards/components/Visualization.js @@ -7,6 +7,7 @@ import buildQueries from 'utils/buildQueriesForGraphs' import VisualizationName from 'src/dashboards/components/VisualizationName' import {getCellTypeColors} from 'src/dashboards/constants/cellEditor' +import {colorsStringSchema, colorsNumberSchema} from 'shared/schemas' const DashVisualization = ( { @@ -75,9 +76,9 @@ DashVisualization.propTypes = { }), tableOptions: shape({}), resizerTopHeight: number, - thresholdsListColors: arrayOf(shape({}).isRequired), - gaugeColors: arrayOf(shape({}).isRequired), - lineColors: arrayOf(shape({}).isRequired), + thresholdsListColors: colorsNumberSchema, + gaugeColors: colorsNumberSchema, + lineColors: colorsStringSchema, staticLegend: bool, setDataLabels: func, } diff --git a/ui/src/dashboards/containers/DashboardPage.js b/ui/src/dashboards/containers/DashboardPage.js index 41154f8da6..45091183af 100644 --- a/ui/src/dashboards/containers/DashboardPage.js +++ b/ui/src/dashboards/containers/DashboardPage.js @@ -37,6 +37,7 @@ import { import {presentationButtonDispatcher} from 'shared/dispatchers' import {DASHBOARD_LAYOUT_ROW_HEIGHT} from 'shared/constants' import {notifyDashboardNotFound} from 'shared/copy/notifications' +import {colorsStringSchema, colorsNumberSchema} from 'shared/schemas' const FORMAT_INFLUXQL = 'influxql' const defaultTimeRange = { @@ -532,9 +533,9 @@ DashboardPage.propTypes = { handleDismissEditingAnnotation: func.isRequired, selectedCell: shape({}), thresholdsListType: string.isRequired, - thresholdsListColors: arrayOf(shape({}).isRequired).isRequired, - gaugeColors: arrayOf(shape({}).isRequired).isRequired, - lineColors: arrayOf(shape({}).isRequired).isRequired, + thresholdsListColors: colorsNumberSchema.isRequired, + gaugeColors: colorsNumberSchema.isRequired, + lineColors: colorsStringSchema.isRequired, } const mapStateToProps = (state, {params: {dashboardID}}) => { diff --git a/ui/src/data_explorer/components/VisView.js b/ui/src/data_explorer/components/VisView.js index 2f71af1ad0..b9359d8584 100644 --- a/ui/src/data_explorer/components/VisView.js +++ b/ui/src/data_explorer/components/VisView.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types' import Table from './Table' import RefreshingGraph from 'shared/components/RefreshingGraph' +import {DEFAULT_LINE_COLORS} from 'src/shared/constants/graphColorPalettes' const VisView = ({ axes, @@ -37,6 +38,7 @@ const VisView = ({ return (
diff --git a/ui/src/shared/components/ColorDropdown.js b/ui/src/shared/components/ColorDropdown.js index d5d4d0a1d8..ddfd5da298 100644 --- a/ui/src/shared/components/ColorDropdown.js +++ b/ui/src/shared/components/ColorDropdown.js @@ -99,7 +99,7 @@ ColorDropdown.propTypes = { shape({ hex: string.isRequired, name: string.isRequired, - }) + }).isRequired ).isRequired, stretchToFit: bool, disabled: bool, diff --git a/ui/src/shared/components/Dygraph.js b/ui/src/shared/components/Dygraph.js index 5b975170be..4ab51dfe1b 100644 --- a/ui/src/shared/components/Dygraph.js +++ b/ui/src/shared/components/Dygraph.js @@ -32,6 +32,8 @@ import { } from 'src/shared/constants/graphColorPalettes' const {LINEAR, LOG, BASE_10, BASE_2} = AXES_SCALE_OPTIONS +import {colorsStringSchema} from 'shared/schemas' + class Dygraph extends Component { constructor(props) { super(props) @@ -458,15 +460,7 @@ 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 - ), + colors: colorsStringSchema.isRequired, } const mapStateToProps = ({annotations: {mode}}) => ({ diff --git a/ui/src/shared/components/Gauge.js b/ui/src/shared/components/Gauge.js index 52ea1614eb..4d1e3b5692 100644 --- a/ui/src/shared/components/Gauge.js +++ b/ui/src/shared/components/Gauge.js @@ -10,6 +10,8 @@ import { MIN_THRESHOLDS, } from 'shared/constants/thresholds' +import {colorsStringSchema} from 'shared/schemas' + class Gauge extends Component { constructor(props) { super(props) @@ -325,21 +327,13 @@ class Gauge extends Component { } } -const {arrayOf, number, shape, string} = PropTypes +const {number, string} = PropTypes Gauge.propTypes = { width: string.isRequired, height: string.isRequired, gaugePosition: number.isRequired, - colors: arrayOf( - shape({ - type: string.isRequired, - hex: string.isRequired, - id: string.isRequired, - name: string.isRequired, - value: string.isRequired, - }).isRequired - ).isRequired, + colors: colorsStringSchema.isRequired, prefix: string.isRequired, suffix: string.isRequired, } diff --git a/ui/src/shared/components/GaugeChart.js b/ui/src/shared/components/GaugeChart.js index 0516d9c11d..cad6d44280 100644 --- a/ui/src/shared/components/GaugeChart.js +++ b/ui/src/shared/components/GaugeChart.js @@ -7,6 +7,8 @@ import {DEFAULT_GAUGE_COLORS} from 'src/shared/constants/thresholds' import {stringifyColorValues} from 'src/shared/constants/colorOperations' import {DASHBOARD_LAYOUT_ROW_HEIGHT} from 'src/shared/constants' +import {colorsStringSchema} from 'shared/schemas' + class GaugeChart extends PureComponent { render() { const { @@ -82,15 +84,7 @@ GaugeChart.propTypes = { cellHeight: number, resizerTopHeight: number, resizeCoords: shape(), - colors: arrayOf( - shape({ - type: string.isRequired, - hex: string.isRequired, - id: string.isRequired, - name: string.isRequired, - value: string.isRequired, - }).isRequired - ), + colors: colorsStringSchema, prefix: string.isRequired, suffix: string.isRequired, } diff --git a/ui/src/shared/components/Layout.js b/ui/src/shared/components/Layout.js index 18ade5ada1..654593492a 100644 --- a/ui/src/shared/components/Layout.js +++ b/ui/src/shared/components/Layout.js @@ -8,6 +8,8 @@ import {IS_STATIC_LEGEND} from 'src/shared/constants' import _ from 'lodash' +import {colorsStringSchema} from 'shared/schemas' + const getSource = (cell, source, sources, defaultSource) => { const s = _.get(cell, ['queries', '0', 'source'], null) if (!s) { @@ -134,15 +136,7 @@ const propTypes = { i: string.isRequired, name: string.isRequired, type: string.isRequired, - colors: arrayOf( - shape({ - type: string.isRequired, - hex: string.isRequired, - id: string.isRequired, - name: string.isRequired, - value: string.isRequired, - }).isRequired - ), + colors: colorsStringSchema, }).isRequired, templates: arrayOf(shape()), host: string, diff --git a/ui/src/shared/components/LineGraph.js b/ui/src/shared/components/LineGraph.js index 87dab79455..c4178c8f7d 100644 --- a/ui/src/shared/components/LineGraph.js +++ b/ui/src/shared/components/LineGraph.js @@ -6,6 +6,8 @@ import shallowCompare from 'react-addons-shallow-compare' import SingleStat from 'src/shared/components/SingleStat' import timeSeriesToDygraph from 'utils/timeSeriesToDygraph' +import {colorsStringSchema} from 'shared/schemas' + class LineGraph extends Component { constructor(props) { super(props) @@ -196,15 +198,7 @@ LineGraph.propTypes = { resizeCoords: shape(), queries: arrayOf(shape({}).isRequired).isRequired, data: arrayOf(shape({}).isRequired).isRequired, - colors: arrayOf( - shape({ - type: string.isRequired, - hex: string.isRequired, - id: string.isRequired, - name: string.isRequired, - value: string.isRequired, - }).isRequired - ), + colors: colorsStringSchema, } export default LineGraph diff --git a/ui/src/shared/components/LineGraphColorSelector.js b/ui/src/shared/components/LineGraphColorSelector.js index 1b197c0987..50ca3c838d 100644 --- a/ui/src/shared/components/LineGraphColorSelector.js +++ b/ui/src/shared/components/LineGraphColorSelector.js @@ -6,6 +6,7 @@ import {bindActionCreators} from 'redux' import ColorScaleDropdown from 'shared/components/ColorScaleDropdown' import {updateLineColors} from 'src/dashboards/actions/cellEditorOverlay' +import {colorsStringSchema} from 'shared/schemas' class LineGraphColorSelector extends Component { handleSelectColors = colorScale => { @@ -31,17 +32,10 @@ class LineGraphColorSelector extends Component { } } -const {arrayOf, func, shape, string} = PropTypes +const {func} = PropTypes LineGraphColorSelector.propTypes = { - lineColors: arrayOf( - shape({ - type: string.isRequired, - hex: string.isRequired, - id: string.isRequired, - name: string.isRequired, - }).isRequired - ).isRequired, + lineColors: colorsStringSchema.isRequired, handleUpdateLineColors: func.isRequired, } diff --git a/ui/src/shared/components/RefreshingGraph.js b/ui/src/shared/components/RefreshingGraph.js index 3468329eea..c3597b618d 100644 --- a/ui/src/shared/components/RefreshingGraph.js +++ b/ui/src/shared/components/RefreshingGraph.js @@ -9,6 +9,8 @@ import SingleStat from 'shared/components/SingleStat' import GaugeChart from 'shared/components/GaugeChart' import TableGraph from 'shared/components/TableGraph' +import {colorsStringSchema} from 'shared/schemas' + const RefreshingLineGraph = AutoRefresh(LineGraph) const RefreshingSingleStat = AutoRefresh(SingleStat) const RefreshingGaugeChart = AutoRefresh(GaugeChart) @@ -154,15 +156,7 @@ RefreshingGraph.propTypes = { onZoom: func, resizeCoords: shape(), grabDataForDownload: func, - colors: arrayOf( - shape({ - type: string.isRequired, - hex: string.isRequired, - id: string.isRequired, - name: string.isRequired, - value: string.isRequired, - }).isRequired - ), + colors: colorsStringSchema, cellID: string, inView: bool, tableOptions: shape({}), diff --git a/ui/src/shared/components/SingleStat.js b/ui/src/shared/components/SingleStat.js index dc91e872db..99e17e12ef 100644 --- a/ui/src/shared/components/SingleStat.js +++ b/ui/src/shared/components/SingleStat.js @@ -6,6 +6,7 @@ import lastValues from 'shared/parsing/lastValues' import {SMALL_CELL_HEIGHT} from 'shared/graphs/helpers' import {DYGRAPH_CONTAINER_V_MARGIN} from 'shared/constants' import {generateThresholdsListHexs} from 'shared/constants/colorOperations' +import {colorsStringSchema} from 'shared/schemas' class SingleStat extends PureComponent { render() { @@ -78,15 +79,7 @@ SingleStat.propTypes = { data: arrayOf(shape()).isRequired, isFetchingInitially: bool, cellHeight: number, - colors: arrayOf( - shape({ - type: string.isRequired, - hex: string.isRequired, - id: string.isRequired, - name: string.isRequired, - value: string.isRequired, - }).isRequired - ), + colors: colorsStringSchema, prefix: string, suffix: string, lineGraph: bool, diff --git a/ui/src/shared/components/TableGraph.js b/ui/src/shared/components/TableGraph.js index f2b8d98c9c..5bf928aab0 100644 --- a/ui/src/shared/components/TableGraph.js +++ b/ui/src/shared/components/TableGraph.js @@ -22,6 +22,7 @@ import { export const DEFAULT_SORT = ASCENDING import {generateThresholdsListHexs} from 'shared/constants/colorOperations' +import {colorsStringSchema} from 'shared/schemas' export const filterInvisibleColumns = (data, fieldNames) => { const visibility = {} @@ -451,15 +452,7 @@ TableGraph.propTypes = { }), hoverTime: string, onSetHoverTime: func, - colors: arrayOf( - shape({ - type: string.isRequired, - hex: string.isRequired, - id: string.isRequired, - name: string.isRequired, - value: string.isRequired, - }).isRequired - ), + colors: colorsStringSchema, setDataLabels: func, } diff --git a/ui/src/shared/components/ThresholdsList.js b/ui/src/shared/components/ThresholdsList.js index 42ba1f9821..669e23fd07 100644 --- a/ui/src/shared/components/ThresholdsList.js +++ b/ui/src/shared/components/ThresholdsList.js @@ -10,6 +10,7 @@ import Threshold from 'src/dashboards/components/Threshold' import ColorDropdown from 'shared/components/ColorDropdown' import {updateThresholdsListColors} from 'src/dashboards/actions/cellEditorOverlay' +import {colorsNumberSchema} from 'shared/schemas' import { THRESHOLD_COLORS, @@ -166,22 +167,14 @@ class ThresholdsList extends Component { ) } } -const {arrayOf, bool, func, number, shape, string} = PropTypes +const {bool, func, string} = PropTypes ThresholdsList.defaultProps = { showListHeading: false, } ThresholdsList.propTypes = { thresholdsListType: string.isRequired, - thresholdsListColors: arrayOf( - shape({ - type: string.isRequired, - hex: string.isRequired, - id: string.isRequired, - name: string.isRequired, - value: number.isRequired, - }).isRequired - ), + thresholdsListColors: colorsNumberSchema.isRequired, handleUpdateThresholdsListColors: func.isRequired, onResetFocus: func.isRequired, showListHeading: bool, diff --git a/ui/src/shared/constants/graphColorPalettes.js b/ui/src/shared/constants/graphColorPalettes.js index 079f45c023..3b3768b4cc 100644 --- a/ui/src/shared/constants/graphColorPalettes.js +++ b/ui/src/shared/constants/graphColorPalettes.js @@ -9,21 +9,21 @@ export const LINE_COLORS_A = [ hex: '#31C0F6', id: '0', name: 'Nineteen Eighty Four', - value: 0, + value: '0', }, { type: COLOR_TYPE_SCALE, hex: '#A500A5', id: '0', name: 'Nineteen Eighty Four', - value: 0, + value: '0', }, { type: COLOR_TYPE_SCALE, hex: '#FF7E27', id: '0', name: 'Nineteen Eighty Four', - value: 0, + value: '0', }, ] @@ -33,21 +33,21 @@ export const LINE_COLORS_B = [ hex: '#74D495', id: '1', name: 'Atlantis', - value: 0, + value: '0', }, { type: COLOR_TYPE_SCALE, hex: '#3F3FBA', id: '1', name: 'Atlantis', - value: 0, + value: '0', }, { type: COLOR_TYPE_SCALE, hex: '#FF4D9E', id: '1', name: 'Atlantis', - value: 0, + value: '0', }, ] @@ -57,21 +57,21 @@ export const LINE_COLORS_C = [ hex: '#8F8AF4', id: '1', name: 'Do Androids Dream of Electric Sheep?', - value: 0, + value: '0', }, { type: COLOR_TYPE_SCALE, hex: '#A51414', id: '1', name: 'Do Androids Dream of Electric Sheep?', - value: 0, + value: '0', }, { type: COLOR_TYPE_SCALE, hex: '#F4CF31', id: '1', name: 'Do Androids Dream of Electric Sheep?', - value: 0, + value: '0', }, ] @@ -81,21 +81,21 @@ export const LINE_COLORS_D = [ hex: '#FD7A5D', id: '1', name: 'Delorean', - value: 0, + value: '0', }, { type: COLOR_TYPE_SCALE, hex: '#5F1CF2', id: '1', name: 'Delorean', - value: 0, + value: '0', }, { type: COLOR_TYPE_SCALE, hex: '#4CE09A', id: '1', name: 'Delorean', - value: 0, + value: '0', }, ] @@ -105,21 +105,21 @@ export const LINE_COLORS_E = [ hex: '#FDC44F', id: '1', name: 'Cthulhu', - value: 0, + value: '0', }, { type: COLOR_TYPE_SCALE, hex: '#007C76', id: '1', name: 'Cthulhu', - value: 0, + value: '0', }, { type: COLOR_TYPE_SCALE, hex: '#8983FF', id: '1', name: 'Cthulhu', - value: 0, + value: '0', }, ] @@ -129,21 +129,21 @@ export const LINE_COLORS_F = [ hex: '#DA6FF1', id: '1', name: 'Ectoplasm', - value: 0, + value: '0', }, { type: COLOR_TYPE_SCALE, hex: '#00717A', id: '1', name: 'Ectoplasm', - value: 0, + value: '0', }, { type: COLOR_TYPE_SCALE, hex: '#ACFF76', id: '1', name: 'Ectoplasm', - value: 0, + value: '0', }, ] @@ -153,21 +153,21 @@ export const LINE_COLORS_G = [ hex: '#F6F6F8', id: '1', name: 'T-Max 400 Film', - value: 0, + value: '0', }, { type: COLOR_TYPE_SCALE, hex: '#A4A8B6', id: '1', name: 'T-Max 400 Film', - value: 0, + value: '0', }, { type: COLOR_TYPE_SCALE, hex: '#545667', id: '1', name: 'T-Max 400 Film', - value: 0, + value: '0', }, ] @@ -177,7 +177,7 @@ export const LINE_COLORS_RULE_GRAPH = [ hex: '#4ED8A0', id: '1', name: 'Rainforest', - value: 0, + value: '0', }, ] diff --git a/ui/src/shared/schemas.js b/ui/src/shared/schemas.js index 35df59a72b..5e16535433 100644 --- a/ui/src/shared/schemas.js +++ b/ui/src/shared/schemas.js @@ -1,6 +1,6 @@ import PropTypes from 'prop-types' -const {shape, string} = PropTypes +const {arrayOf, number, shape, string} = PropTypes export const annotation = shape({ id: string.isRequired, @@ -9,3 +9,23 @@ export const annotation = shape({ text: string.isRequired, type: string.isRequired, }) + +export const colorsStringSchema = arrayOf( + shape({ + type: string.isRequired, + hex: string.isRequired, + id: string.isRequired, + name: string.isRequired, + value: string.isRequired, + }).isRequired +) + +export const colorsNumberSchema = arrayOf( + shape({ + type: string.isRequired, + hex: string.isRequired, + id: string.isRequired, + name: string.isRequired, + value: number.isRequired, + }).isRequired +) diff --git a/ui/src/status/fixtures.js b/ui/src/status/fixtures.js index d366801897..3e96f036f0 100644 --- a/ui/src/status/fixtures.js +++ b/ui/src/status/fixtures.js @@ -1,3 +1,5 @@ +import {DEFAULT_LINE_COLORS} from 'src/shared/constants/graphColorPalettes' + export const fixtureStatusPageCells = [ { i: 'alerts-bar-graph', @@ -8,6 +10,7 @@ export const fixtureStatusPageCells = [ h: 4, legend: {}, name: 'Alert Events per Day – Last 30 Days', + colors: DEFAULT_LINE_COLORS, queries: [ { query: