Merge pull request #3162 from influxdata/fix/graph-threshold-size
Only send threshold value to parent on blurfeature/cell-notes
commit
e2f7fcfe1a
|
@ -30,6 +30,7 @@
|
|||
1. [#3144](https://github.com/influxdata/chronograf/pull/3144): Ensure correct basepath prefix in URL pathname when passing InfluxQL query param to Data Explorer
|
||||
1. [#3128](https://github.com/influxdata/chronograf/pull/3128): Fix type error bug in Kapacitor Alert Config page and persist deleting of team and recipient in OpsGenieConfig
|
||||
1. [#3149](https://github.com/influxdata/chronograf/pull/3149): Fixes errors caused by switching query tabs in CEO
|
||||
1. [#3162](https://github.com/influxdata/chronograf/pull/3162): Only send threshold value to parent on blur
|
||||
|
||||
## v1.4.3.1 [2018-04-02]
|
||||
|
||||
|
|
|
@ -123,7 +123,7 @@ class GaugeOptions extends Component {
|
|||
)
|
||||
|
||||
const isUnique = !colorsWithoutMinOrMax.some(
|
||||
color => color.value === targetValue
|
||||
color => color.value === targetValue && color.id !== threshold.id
|
||||
)
|
||||
|
||||
allowedToUpdate = greaterThanMin && lessThanMax && isUnique
|
||||
|
@ -146,11 +146,11 @@ class GaugeOptions extends Component {
|
|||
handleUpdateAxes(newAxes)
|
||||
}
|
||||
|
||||
handleSortColors = () => {
|
||||
const {gaugeColors, handleUpdateGaugeColors} = this.props
|
||||
const sortedColors = _.sortBy(gaugeColors, color => color.value)
|
||||
get sortedGaugeColors() {
|
||||
const {gaugeColors} = this.props
|
||||
const sortedColors = _.sortBy(gaugeColors, 'value')
|
||||
|
||||
handleUpdateGaugeColors(sortedColors)
|
||||
return sortedColors
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -174,12 +174,10 @@ class GaugeOptions extends Component {
|
|||
>
|
||||
<span className="icon plus" /> Add Threshold
|
||||
</button>
|
||||
{gaugeColors.map(color => (
|
||||
{this.sortedGaugeColors.map((color, index) => (
|
||||
<Threshold
|
||||
isMin={color.value === gaugeColors[0].value}
|
||||
isMax={
|
||||
color.value === gaugeColors[gaugeColors.length - 1].value
|
||||
}
|
||||
isMin={index === 0}
|
||||
isMax={index === gaugeColors.length - 1}
|
||||
visualizationType="gauge"
|
||||
threshold={color}
|
||||
key={uuid.v4()}
|
||||
|
@ -188,7 +186,6 @@ class GaugeOptions extends Component {
|
|||
onValidateColorValue={this.handleValidateColorValue}
|
||||
onUpdateColorValue={this.handleUpdateColorValue}
|
||||
onDeleteThreshold={this.handleDeleteThreshold}
|
||||
onSortColors={this.handleSortColors}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
@ -1,131 +0,0 @@
|
|||
import React, {Component} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import ColorDropdown from 'shared/components/ColorDropdown'
|
||||
|
||||
import {THRESHOLD_COLORS} from 'shared/constants/thresholds'
|
||||
|
||||
class Threshold extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
workingValue: this.props.threshold.value,
|
||||
valid: true,
|
||||
}
|
||||
}
|
||||
|
||||
handleChangeWorkingValue = e => {
|
||||
const {threshold, onValidateColorValue, onUpdateColorValue} = this.props
|
||||
const targetValue = Number(e.target.value)
|
||||
|
||||
const valid = onValidateColorValue(threshold, targetValue)
|
||||
|
||||
if (valid) {
|
||||
onUpdateColorValue(threshold, targetValue)
|
||||
}
|
||||
|
||||
this.setState({valid, workingValue: targetValue})
|
||||
}
|
||||
|
||||
handleBlur = () => {
|
||||
this.setState({workingValue: this.props.threshold.value, valid: true})
|
||||
this.props.onSortColors()
|
||||
}
|
||||
|
||||
handleKeyUp = e => {
|
||||
if (e.key === 'Enter') {
|
||||
this.thresholdInputRef.blur()
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
visualizationType,
|
||||
threshold,
|
||||
threshold: {hex, name},
|
||||
disableMaxColor,
|
||||
onChooseColor,
|
||||
onDeleteThreshold,
|
||||
isMin,
|
||||
isMax,
|
||||
} = this.props
|
||||
const {workingValue, valid} = this.state
|
||||
const selectedColor = {hex, name}
|
||||
|
||||
let label = 'Threshold'
|
||||
let labelClass = 'threshold-item--label__editable'
|
||||
let canBeDeleted = true
|
||||
|
||||
if (visualizationType === 'gauge') {
|
||||
labelClass =
|
||||
isMin || isMax
|
||||
? 'threshold-item--label'
|
||||
: 'threshold-item--label__editable'
|
||||
canBeDeleted = !(isMin || isMax)
|
||||
}
|
||||
|
||||
if (isMin && visualizationType === 'gauge') {
|
||||
label = 'Minimum'
|
||||
}
|
||||
if (isMax && visualizationType === 'gauge') {
|
||||
label = 'Maximum'
|
||||
}
|
||||
|
||||
const inputClass = valid
|
||||
? 'form-control input-sm threshold-item--input'
|
||||
: 'form-control input-sm threshold-item--input form-volcano'
|
||||
|
||||
return (
|
||||
<div className="threshold-item">
|
||||
<div className={labelClass}>{label}</div>
|
||||
{canBeDeleted ? (
|
||||
<button
|
||||
className="btn btn-default btn-sm btn-square"
|
||||
onClick={onDeleteThreshold(threshold)}
|
||||
>
|
||||
<span className="icon remove" />
|
||||
</button>
|
||||
) : null}
|
||||
<input
|
||||
value={workingValue}
|
||||
className={inputClass}
|
||||
type="number"
|
||||
onChange={this.handleChangeWorkingValue}
|
||||
onBlur={this.handleBlur}
|
||||
onKeyUp={this.handleKeyUp}
|
||||
ref={r => (this.thresholdInputRef = r)}
|
||||
/>
|
||||
<ColorDropdown
|
||||
colors={THRESHOLD_COLORS}
|
||||
selected={selectedColor}
|
||||
onChoose={onChooseColor(threshold)}
|
||||
disabled={isMax && disableMaxColor}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const {bool, func, number, shape, string} = PropTypes
|
||||
|
||||
Threshold.propTypes = {
|
||||
visualizationType: string.isRequired,
|
||||
threshold: shape({
|
||||
type: string.isRequired,
|
||||
hex: string.isRequired,
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
value: number.isRequired,
|
||||
}).isRequired,
|
||||
disableMaxColor: bool,
|
||||
onChooseColor: func.isRequired,
|
||||
onValidateColorValue: func.isRequired,
|
||||
onUpdateColorValue: func.isRequired,
|
||||
onDeleteThreshold: func.isRequired,
|
||||
isMin: bool,
|
||||
isMax: bool,
|
||||
onSortColors: func.isRequired,
|
||||
}
|
||||
|
||||
export default Threshold
|
|
@ -0,0 +1,176 @@
|
|||
import React, {PureComponent, ChangeEvent, KeyboardEvent} from 'react'
|
||||
|
||||
import ColorDropdown from 'src/shared/components/ColorDropdown'
|
||||
import {THRESHOLD_COLORS} from 'src/shared/constants/thresholds'
|
||||
|
||||
interface SelectedColor {
|
||||
hex: string
|
||||
name: string
|
||||
}
|
||||
interface ThresholdProps {
|
||||
type: string
|
||||
hex: string
|
||||
id: string
|
||||
name: string
|
||||
value: number
|
||||
}
|
||||
|
||||
interface Props {
|
||||
visualizationType: string
|
||||
threshold: ThresholdProps
|
||||
disableMaxColor: boolean
|
||||
onChooseColor: (threshold: ThresholdProps) => void
|
||||
onValidateColorValue: (
|
||||
threshold: ThresholdProps,
|
||||
targetValue: number
|
||||
) => boolean
|
||||
onUpdateColorValue: (threshold: ThresholdProps, targetValue: number) => void
|
||||
onDeleteThreshold: (threshold: ThresholdProps) => void
|
||||
isMin: boolean
|
||||
isMax: boolean
|
||||
}
|
||||
|
||||
interface State {
|
||||
workingValue: number
|
||||
valid: boolean
|
||||
}
|
||||
|
||||
class Threshold extends PureComponent<Props, State> {
|
||||
private thresholdInputRef: HTMLInputElement
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
workingValue: this.props.threshold.value,
|
||||
valid: true,
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {threshold, disableMaxColor, onChooseColor, isMax} = this.props
|
||||
const {workingValue} = this.state
|
||||
|
||||
return (
|
||||
<div className="threshold-item">
|
||||
<div className={this.labelClass}>{this.label}</div>
|
||||
{this.canBeDeleted ? (
|
||||
<button
|
||||
className="btn btn-default btn-sm btn-square"
|
||||
onClick={this.handleDelete}
|
||||
>
|
||||
<span className="icon remove" />
|
||||
</button>
|
||||
) : null}
|
||||
<input
|
||||
value={workingValue}
|
||||
className={this.inputClass}
|
||||
type="number"
|
||||
onChange={this.handleChangeWorkingValue}
|
||||
onBlur={this.handleBlur}
|
||||
onKeyUp={this.handleKeyUp}
|
||||
ref={this.handleInputRef}
|
||||
/>
|
||||
<ColorDropdown
|
||||
colors={THRESHOLD_COLORS}
|
||||
selected={this.selectedColor}
|
||||
onChoose={onChooseColor(threshold)}
|
||||
disabled={isMax && disableMaxColor}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
private get selectedColor(): SelectedColor {
|
||||
const {threshold: {hex, name}} = this.props
|
||||
return {hex, name}
|
||||
}
|
||||
|
||||
private get inputClass(): string {
|
||||
const {valid} = this.state
|
||||
|
||||
const inputClass = valid
|
||||
? 'form-control input-sm threshold-item--input'
|
||||
: 'form-control input-sm threshold-item--input form-volcano'
|
||||
|
||||
return inputClass
|
||||
}
|
||||
|
||||
private get canBeDeleted(): boolean {
|
||||
const {visualizationType, isMax, isMin} = this.props
|
||||
|
||||
let canBeDeleted = true
|
||||
|
||||
if (visualizationType === 'gauge') {
|
||||
canBeDeleted = !(isMin || isMax)
|
||||
}
|
||||
|
||||
return canBeDeleted
|
||||
}
|
||||
|
||||
private get labelClass(): string {
|
||||
const {visualizationType, isMax, isMin} = this.props
|
||||
|
||||
let labelClass = 'threshold-item--label__editable'
|
||||
|
||||
if (visualizationType === 'gauge') {
|
||||
labelClass =
|
||||
isMin || isMax
|
||||
? 'threshold-item--label'
|
||||
: 'threshold-item--label__editable'
|
||||
}
|
||||
|
||||
return labelClass
|
||||
}
|
||||
|
||||
private get label(): string {
|
||||
let label = 'Threshold'
|
||||
const {visualizationType, isMax, isMin} = this.props
|
||||
|
||||
if (isMin && visualizationType === 'gauge') {
|
||||
label = 'Minimum'
|
||||
}
|
||||
if (isMax && visualizationType === 'gauge') {
|
||||
label = 'Maximum'
|
||||
}
|
||||
|
||||
return label
|
||||
}
|
||||
|
||||
private handleChangeWorkingValue = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
const {threshold, onValidateColorValue} = this.props
|
||||
const targetValue = Number(e.target.value)
|
||||
|
||||
const valid = onValidateColorValue(threshold, targetValue)
|
||||
|
||||
this.setState({valid, workingValue: targetValue})
|
||||
}
|
||||
|
||||
private handleBlur = () => {
|
||||
const {valid, workingValue} = this.state
|
||||
const {threshold, onUpdateColorValue} = this.props
|
||||
|
||||
if (valid) {
|
||||
onUpdateColorValue(threshold, workingValue)
|
||||
} else {
|
||||
this.setState({workingValue: threshold.value, valid: true})
|
||||
}
|
||||
}
|
||||
|
||||
private handleKeyUp = (e: KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.key === 'Enter') {
|
||||
this.thresholdInputRef.blur()
|
||||
}
|
||||
}
|
||||
|
||||
private handleInputRef = (ref: HTMLInputElement) => {
|
||||
this.thresholdInputRef = ref
|
||||
}
|
||||
|
||||
private handleDelete = () => {
|
||||
const {threshold, onDeleteThreshold} = this.props
|
||||
onDeleteThreshold(threshold)
|
||||
}
|
||||
}
|
||||
|
||||
export default Threshold
|
|
@ -113,11 +113,11 @@ class ThresholdsList extends Component {
|
|||
return !sortedColors.some(color => color.value === targetValue)
|
||||
}
|
||||
|
||||
handleSortColors = () => {
|
||||
const {thresholdsListColors, handleUpdateThresholdsListColors} = this.props
|
||||
const sortedColors = _.sortBy(thresholdsListColors, color => color.value)
|
||||
get sortedColors() {
|
||||
const {thresholdsListColors} = this.props
|
||||
const sortedColors = _.sortBy(thresholdsListColors, 'value')
|
||||
|
||||
handleUpdateThresholdsListColors(sortedColors)
|
||||
return sortedColors
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -138,7 +138,7 @@ class ThresholdsList extends Component {
|
|||
>
|
||||
<span className="icon plus" /> Add Threshold
|
||||
</button>
|
||||
{thresholdsListColors.map(
|
||||
{this.sortedColors.map(
|
||||
color =>
|
||||
color.id === THRESHOLD_TYPE_BASE ? (
|
||||
<div className="threshold-item" key={uuid.v4()}>
|
||||
|
@ -159,7 +159,6 @@ class ThresholdsList extends Component {
|
|||
onValidateColorValue={this.handleValidateColorValue}
|
||||
onUpdateColorValue={this.handleUpdateColorValue}
|
||||
onDeleteThreshold={this.handleDeleteThreshold}
|
||||
onSortColors={this.handleSortColors}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
import ThresholdProps from 'src/dashboards/components/Threshold'
|
||||
|
||||
import React from 'react'
|
||||
import {shallow} from 'enzyme'
|
||||
|
||||
describe('Threshold', () => {
|
||||
it('renders without an error', () => {
|
||||
const props = {
|
||||
visualizationType: 'gauge',
|
||||
threshold: {
|
||||
type: 'color',
|
||||
hex: '#444444',
|
||||
id: 'id',
|
||||
name: 'name',
|
||||
value: 2,
|
||||
},
|
||||
disableMaxColor: true,
|
||||
onChooseColor: () => {},
|
||||
onValidateColorValue: () => true,
|
||||
onUpdateColorValue: () => {},
|
||||
onDeleteThreshold: () => {},
|
||||
isMin: false,
|
||||
isMax: true,
|
||||
}
|
||||
|
||||
const wrapper = shallow(<ThresholdProps {...props} />)
|
||||
expect(wrapper.exists()).toBe(true)
|
||||
})
|
||||
})
|
Loading…
Reference in New Issue