From 5dc6cd4852c0d10c4dbb26a0797ec6cf15b6dab8 Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 7 Feb 2018 17:03:19 -0800 Subject: [PATCH 01/16] Use separate state for gauge and single-stat colors and introduce concept of base color in single-stat Somewhat easier to reason through the code, although bulkier. The feature should now mimic the previous appearance of single-stat graphs by default (aka blue text) --- .../components/CellEditorOverlay.js | 202 +++++++++++++----- .../dashboards/components/DisplayOptions.js | 42 ++-- .../components/SingleStatOptions.js | 66 ++++-- ui/src/dashboards/constants/gaugeColors.js | 80 +++++-- ui/src/shared/components/ColorDropdown.js | 10 +- ui/src/shared/components/GaugeChart.js | 4 +- .../style/components/ceo-display-options.scss | 10 +- ui/src/style/components/color-dropdown.scss | 4 + 8 files changed, 296 insertions(+), 122 deletions(-) diff --git a/ui/src/dashboards/components/CellEditorOverlay.js b/ui/src/dashboards/components/CellEditorOverlay.js index 8432c8f316..1d392d6c22 100644 --- a/ui/src/dashboards/components/CellEditorOverlay.js +++ b/ui/src/dashboards/components/CellEditorOverlay.js @@ -28,9 +28,9 @@ import { DEFAULT_VALUE_MIN, DEFAULT_VALUE_MAX, GAUGE_COLORS, - SINGLE_STAT_TEXT, - SINGLE_STAT_BG, - validateColors, + validateGaugeColors, + validateSingleStatColors, + getSingleStatColoration, } from 'src/dashboards/constants/gaugeColors' class CellEditorOverlay extends Component { @@ -49,7 +49,8 @@ class CellEditorOverlay extends Component { source, })) ) - const colorsTypeContainsText = _.some(colors, {type: SINGLE_STAT_TEXT}) + + const singleStatColoration = getSingleStatColoration(colors) this.state = { cellWorkingName: name, @@ -58,8 +59,13 @@ class CellEditorOverlay extends Component { activeQueryIndex: 0, isDisplayOptionsTabActive: false, axes, - colorSingleStatText: colorsTypeContainsText, - colors: validateColors(colors, type, colorsTypeContainsText), + singleStatColoration, + gaugeColors: validateGaugeColors(colors, type), + singleStatColors: validateSingleStatColors( + colors, + type, + singleStatColoration + ), } } @@ -80,23 +86,17 @@ class CellEditorOverlay extends Component { this.overlayRef.focus() } - handleAddThreshold = () => { - const {colors, cellWorkingType} = this.state - const sortedColors = _.sortBy(colors, color => Number(color.value)) + handleAddGaugeThreshold = () => { + const {gaugeColors} = this.state + const sortedColors = _.sortBy(gaugeColors, color => Number(color.value)) if (sortedColors.length <= MAX_THRESHOLDS) { const randomColor = _.random(0, GAUGE_COLORS.length - 1) - const maxValue = - cellWorkingType === 'gauge' - ? Number(sortedColors[sortedColors.length - 1].value) - : DEFAULT_VALUE_MAX - const minValue = - cellWorkingType === 'gauge' - ? Number(sortedColors[0].value) - : DEFAULT_VALUE_MIN + const maxValue = Number(sortedColors[sortedColors.length - 1].value) + const minValue = Number(sortedColors[0].value) - const colorsValues = _.mapValues(colors, 'value') + const colorsValues = _.mapValues(gaugeColors, 'value') let randomValue do { @@ -111,51 +111,122 @@ class CellEditorOverlay extends Component { name: GAUGE_COLORS[randomColor].name, } - this.setState({colors: [...colors, newThreshold]}) + this.setState({gaugeColors: [...gaugeColors, newThreshold]}) } } + handleAddSingleStatThreshold = () => { + const {singleStatColors, singleStatColoration} = this.state + + const randomColor = _.random(0, GAUGE_COLORS.length - 1) + + const maxValue = Number(DEFAULT_VALUE_MIN) + const minValue = Number(DEFAULT_VALUE_MAX) + + let randomValue = _.round(_.random(minValue, maxValue, true), 2) + + if (singleStatColors.length > 0) { + const colorsValues = _.mapValues(singleStatColors, 'value') + do { + randomValue = `${_.round(_.random(minValue, maxValue, true), 2)}` + } while (_.includes(colorsValues, randomValue)) + } + + const newThreshold = { + type: singleStatColoration, + id: uuid.v4(), + value: randomValue, + hex: GAUGE_COLORS[randomColor].hex, + name: GAUGE_COLORS[randomColor].name, + } + + this.setState({singleStatColors: [...singleStatColors, newThreshold]}) + } + handleDeleteThreshold = threshold => () => { - const {colors} = this.state + const {cellWorkingType} = this.state - const newColors = colors.filter(color => color.id !== threshold.id) + if (cellWorkingType === 'gauge') { + const gaugeColors = this.state.gaugeColors.filter( + color => color.id !== threshold.id + ) - this.setState({colors: newColors}) + this.setState({gaugeColors}) + } + + if (cellWorkingType === 'single-stat') { + const singleStatColors = this.state.singleStatColors.filter( + color => color.id !== threshold.id + ) + + this.setState({singleStatColors}) + } } handleChooseColor = threshold => chosenColor => { - const {colors} = this.state + const {cellWorkingType} = this.state - const newColors = colors.map( - color => - color.id === threshold.id - ? {...color, hex: chosenColor.hex, name: chosenColor.name} - : color - ) + if (cellWorkingType === 'gauge') { + const gaugeColors = this.state.gaugeColors.map( + color => + color.id === threshold.id + ? {...color, hex: chosenColor.hex, name: chosenColor.name} + : color + ) - this.setState({colors: newColors}) + this.setState({gaugeColors}) + } + + if (cellWorkingType === 'single-stat') { + const singleStatColors = this.state.singleStatColors.map( + color => + color.id === threshold.id + ? {...color, hex: chosenColor.hex, name: chosenColor.name} + : color + ) + + this.setState({singleStatColors}) + } } handleUpdateColorValue = (threshold, newValue) => { - const {colors} = this.state - const newColors = colors.map( - color => (color.id === threshold.id ? {...color, value: newValue} : color) - ) - this.setState({colors: newColors}) + const {cellWorkingType} = this.state + + if (cellWorkingType === 'gauge') { + const gaugeColors = this.state.gaugeColors.map( + color => + color.id === threshold.id ? {...color, value: newValue} : color + ) + + this.setState({gaugeColors}) + } + + if (cellWorkingType === 'single-stat') { + const singleStatColors = this.state.singleStatColors.map( + color => + color.id === threshold.id ? {...color, value: newValue} : color + ) + + this.setState({singleStatColors}) + } } handleValidateColorValue = (threshold, e) => { - const {colors, cellWorkingType} = this.state - const sortedColors = _.sortBy(colors, color => Number(color.value)) + const {gaugeColors, singleStatColors, cellWorkingType} = this.state const thresholdValue = Number(threshold.value) const targetValueNumber = Number(e.target.value) let allowedToUpdate = false if (cellWorkingType === 'single-stat') { // If type is single-stat then value only has to be unique + const sortedColors = _.sortBy(singleStatColors, color => + Number(color.value) + ) return !sortedColors.some(color => color.value === e.target.value) } + const sortedColors = _.sortBy(gaugeColors, color => Number(color.value)) + const minValue = Number(sortedColors[0].value) const maxValue = Number(sortedColors[sortedColors.length - 1].value) @@ -189,16 +260,15 @@ class CellEditorOverlay extends Component { return allowedToUpdate } - handleToggleSingleStatText = () => { - const {colors, colorSingleStatText} = this.state - const formattedColors = colors.map(color => ({ + handleToggleSingleStatColoration = newColoration => () => { + const singleStatColors = this.state.singleStatColors.map(color => ({ ...color, - type: colorSingleStatText ? SINGLE_STAT_BG : SINGLE_STAT_TEXT, + type: newColoration, })) this.setState({ - colorSingleStatText: !colorSingleStatText, - colors: formattedColors, + singleStatColoration: newColoration, + singleStatColors, }) } @@ -298,7 +368,8 @@ class CellEditorOverlay extends Component { cellWorkingType: type, cellWorkingName: name, axes, - colors, + gaugeColors, + singleStatColors, } = this.state const {cell} = this.props @@ -314,6 +385,13 @@ class CellEditorOverlay extends Component { } }) + let colors = [] + if (type === 'gauge') { + colors = gaugeColors + } else if (type === 'single-stat') { + colors = singleStatColors + } + this.props.onSave({ ...cell, name, @@ -324,14 +402,8 @@ class CellEditorOverlay extends Component { }) } - handleSelectGraphType = graphType => () => { - const {colors, colorSingleStatText} = this.state - const validatedColors = validateColors( - colors, - graphType, - colorSingleStatText - ) - this.setState({cellWorkingType: graphType, colors: validatedColors}) + handleSelectGraphType = cellWorkingType => () => { + this.setState({cellWorkingType}) } handleClickDisplayOptionsTab = isDisplayOptionsTabActive => () => { @@ -474,13 +546,14 @@ class CellEditorOverlay extends Component { const { axes, - colors, + gaugeColors, + singleStatColors, activeQueryIndex, cellWorkingName, cellWorkingType, isDisplayOptionsTabActive, queriesWorkingDraft, - colorSingleStatText, + singleStatColoration, } = this.state const queryActions = { @@ -492,6 +565,13 @@ class CellEditorOverlay extends Component { (!!query.measurement && !!query.database && !!query.fields.length) || !!query.rawText + let visualizationColors + if (cellWorkingType === 'gauge') { + visualizationColors = gaugeColors + } else if (cellWorkingType === 'single-stat') { + visualizationColors = singleStatColors + } + return (
{ const { - colors, + gaugeColors, + singleStatColors, onSetBase, onSetScale, onSetLabel, @@ -43,13 +44,14 @@ class DisplayOptions extends Component { onSetPrefixSuffix, onSetYAxisBoundMin, onSetYAxisBoundMax, - onAddThreshold, + onAddGaugeThreshold, + onAddSingleStatThreshold, onDeleteThreshold, onChooseColor, onValidateColorValue, onUpdateColorValue, - colorSingleStatText, - onToggleSingleStatText, + singleStatColoration, + onToggleSingleStatColoration, onSetSuffix, } = this.props const {axes, axes: {y: {suffix}}} = this.state @@ -58,27 +60,27 @@ class DisplayOptions extends Component { case 'gauge': return ( ) case 'single-stat': return ( ) default: @@ -111,10 +113,11 @@ class DisplayOptions extends Component { ) } } -const {arrayOf, bool, func, shape, string} = PropTypes +const {arrayOf, func, shape, string} = PropTypes DisplayOptions.propTypes = { - onAddThreshold: func.isRequired, + onAddGaugeThreshold: func.isRequired, + onAddSingleStatThreshold: func.isRequired, onDeleteThreshold: func.isRequired, onChooseColor: func.isRequired, onValidateColorValue: func.isRequired, @@ -129,7 +132,16 @@ DisplayOptions.propTypes = { onSetLabel: func.isRequired, onSetBase: func.isRequired, axes: shape({}).isRequired, - colors: arrayOf( + gaugeColors: arrayOf( + shape({ + type: string.isRequired, + hex: string.isRequired, + id: string.isRequired, + name: string.isRequired, + value: string.isRequired, + }).isRequired + ), + singleStatColors: arrayOf( shape({ type: string.isRequired, hex: string.isRequired, @@ -139,8 +151,8 @@ DisplayOptions.propTypes = { }).isRequired ), queryConfigs: arrayOf(shape()).isRequired, - colorSingleStatText: bool.isRequired, - onToggleSingleStatText: func.isRequired, + singleStatColoration: string.isRequired, + onToggleSingleStatColoration: func.isRequired, } export default DisplayOptions diff --git a/ui/src/dashboards/components/SingleStatOptions.js b/ui/src/dashboards/components/SingleStatOptions.js index 2ee686c4ed..2821cdfc1b 100644 --- a/ui/src/dashboards/components/SingleStatOptions.js +++ b/ui/src/dashboards/components/SingleStatOptions.js @@ -3,9 +3,20 @@ import _ from 'lodash' import FancyScrollbar from 'shared/components/FancyScrollbar' import Threshold from 'src/dashboards/components/Threshold' +import ColorDropdown from 'shared/components/ColorDropdown' -import {MAX_THRESHOLDS} from 'src/dashboards/constants/gaugeColors' +import { + GAUGE_COLORS, + MAX_THRESHOLDS, + SINGLE_STAT_BASE, + SINGLE_STAT_TEXT, + SINGLE_STAT_BG, +} from 'src/dashboards/constants/gaugeColors' +const formatColor = color => { + const {hex, name} = color + return {hex, name} +} const SingleStatOptions = ({ suffix, onSetSuffix, @@ -15,8 +26,8 @@ const SingleStatOptions = ({ onChooseColor, onValidateColorValue, onUpdateColorValue, - colorSingleStatText, - onToggleSingleStatText, + singleStatColoration, + onToggleSingleStatColoration, }) => { const disableAddThreshold = colors.length > MAX_THRESHOLDS @@ -37,16 +48,27 @@ const SingleStatOptions = ({ > Add Threshold - {sortedColors.map(color => - + {sortedColors.map( + color => + color.id === SINGLE_STAT_BASE + ?
+
Base Color
+ +
+ : )}
@@ -54,14 +76,18 @@ const SingleStatOptions = ({