Merge branch 'master' into feature/graph-table

feature/graph-table-CEO
Iris Scholten 2018-02-27 17:18:00 -08:00
commit 9afed4099e
11 changed files with 103 additions and 39 deletions

View File

@ -249,6 +249,10 @@ class CellEditorOverlay extends Component {
} }
} }
handleResetFocus = () => {
this.overlayRef.focus()
}
render() { render() {
const { const {
onCancel, onCancel,
@ -313,6 +317,7 @@ class CellEditorOverlay extends Component {
queryConfigs={queriesWorkingDraft} queryConfigs={queriesWorkingDraft}
onToggleStaticLegend={this.handleToggleStaticLegend} onToggleStaticLegend={this.handleToggleStaticLegend}
staticLegend={staticLegend} staticLegend={staticLegend}
onResetFocus={this.handleResetFocus}
/> />
: <QueryMaker : <QueryMaker
source={this.getSource()} source={this.getSource()}

View File

@ -36,12 +36,17 @@ class DisplayOptions extends Component {
} }
renderOptions = () => { renderOptions = () => {
const {cell: {type}, staticLegend, onToggleStaticLegend} = this.props const {
cell: {type},
staticLegend,
onToggleStaticLegend,
onResetFocus,
} = this.props
switch (type) { switch (type) {
case 'gauge': case 'gauge':
return <GaugeOptions /> return <GaugeOptions onResetFocus={onResetFocus} />
case 'single-stat': case 'single-stat':
return <SingleStatOptions /> return <SingleStatOptions onResetFocus={onResetFocus} />
case 'table': case 'table':
return <TableOptions /> return <TableOptions />
default: default:
@ -80,6 +85,7 @@ DisplayOptions.propTypes = {
queryConfigs: arrayOf(shape()).isRequired, queryConfigs: arrayOf(shape()).isRequired,
onToggleStaticLegend: func.isRequired, onToggleStaticLegend: func.isRequired,
staticLegend: bool, staticLegend: bool,
onResetFocus: func.isRequired,
} }
const mapStateToProps = ({cellEditorOverlay: {cell, cell: {axes}}}) => ({ const mapStateToProps = ({cellEditorOverlay: {cell, cell: {axes}}}) => ({

View File

@ -22,7 +22,7 @@ import {
class GaugeOptions extends Component { class GaugeOptions extends Component {
handleAddThreshold = () => { handleAddThreshold = () => {
const {gaugeColors, handleUpdateGaugeColors} = this.props const {gaugeColors, handleUpdateGaugeColors, onResetFocus} = this.props
const sortedColors = _.sortBy(gaugeColors, color => color.value) const sortedColors = _.sortBy(gaugeColors, color => color.value)
if (sortedColors.length <= MAX_THRESHOLDS) { if (sortedColors.length <= MAX_THRESHOLDS) {
@ -47,16 +47,19 @@ class GaugeOptions extends Component {
} }
handleUpdateGaugeColors([...gaugeColors, newThreshold]) handleUpdateGaugeColors([...gaugeColors, newThreshold])
} else {
onResetFocus()
} }
} }
handleDeleteThreshold = threshold => () => { handleDeleteThreshold = threshold => () => {
const {handleUpdateGaugeColors} = this.props const {handleUpdateGaugeColors, onResetFocus} = this.props
const gaugeColors = this.props.gaugeColors.filter( const gaugeColors = this.props.gaugeColors.filter(
color => color.id !== threshold.id color => color.id !== threshold.id
) )
handleUpdateGaugeColors(gaugeColors) handleUpdateGaugeColors(gaugeColors)
onResetFocus()
} }
handleChooseColor = threshold => chosenColor => { handleChooseColor = threshold => chosenColor => {
@ -217,6 +220,7 @@ GaugeOptions.propTypes = {
handleUpdateGaugeColors: func.isRequired, handleUpdateGaugeColors: func.isRequired,
handleUpdateAxes: func.isRequired, handleUpdateAxes: func.isRequired,
axes: shape({}).isRequired, axes: shape({}).isRequired,
onResetFocus: func.isRequired,
} }
const mapStateToProps = ({cellEditorOverlay: {gaugeColors, cell: {axes}}}) => ({ const mapStateToProps = ({cellEditorOverlay: {gaugeColors, cell: {axes}}}) => ({

View File

@ -42,6 +42,7 @@ class SingleStatOptions extends Component {
singleStatColors, singleStatColors,
singleStatType, singleStatType,
handleUpdateSingleStatColors, handleUpdateSingleStatColors,
onResetFocus,
} = this.props } = this.props
const randomColor = _.random(0, GAUGE_COLORS.length - 1) const randomColor = _.random(0, GAUGE_COLORS.length - 1)
@ -67,16 +68,18 @@ class SingleStatOptions extends Component {
} }
handleUpdateSingleStatColors([...singleStatColors, newThreshold]) handleUpdateSingleStatColors([...singleStatColors, newThreshold])
onResetFocus()
} }
handleDeleteThreshold = threshold => () => { handleDeleteThreshold = threshold => () => {
const {handleUpdateSingleStatColors} = this.props const {handleUpdateSingleStatColors, onResetFocus} = this.props
const singleStatColors = this.props.singleStatColors.filter( const singleStatColors = this.props.singleStatColors.filter(
color => color.id !== threshold.id color => color.id !== threshold.id
) )
handleUpdateSingleStatColors(singleStatColors) handleUpdateSingleStatColors(singleStatColors)
onResetFocus()
} }
handleChooseColor = threshold => chosenColor => { handleChooseColor = threshold => chosenColor => {
@ -242,6 +245,7 @@ SingleStatOptions.propTypes = {
handleUpdateSingleStatColors: func.isRequired, handleUpdateSingleStatColors: func.isRequired,
handleUpdateAxes: func.isRequired, handleUpdateAxes: func.isRequired,
axes: shape({}).isRequired, axes: shape({}).isRequired,
onResetFocus: func.isRequired,
} }
const mapStateToProps = ({ const mapStateToProps = ({

View File

@ -19,6 +19,7 @@ const Annotation = ({
annotation={annotation} annotation={annotation}
mode={mode} mode={mode}
dygraph={dygraph} dygraph={dygraph}
staticLegendHeight={staticLegendHeight}
/> />
: <AnnotationSpan : <AnnotationSpan
lastUpdated={lastUpdated} lastUpdated={lastUpdated}

View File

@ -1,7 +1,11 @@
import React, {PropTypes} from 'react' import React, {PropTypes} from 'react'
import {connect} from 'react-redux' import {connect} from 'react-redux'
import {DYGRAPH_CONTAINER_MARGIN} from 'shared/constants' import {
DYGRAPH_CONTAINER_H_MARGIN,
DYGRAPH_CONTAINER_V_MARGIN,
DYGRAPH_CONTAINER_XLABEL_MARGIN,
} from 'shared/constants'
import {ANNOTATION_MIN_DELTA, EDITING} from 'shared/annotations/helpers' import {ANNOTATION_MIN_DELTA, EDITING} from 'shared/annotations/helpers'
import * as schema from 'shared/schemas' import * as schema from 'shared/schemas'
import * as actions from 'shared/actions/annotations' import * as actions from 'shared/actions/annotations'
@ -85,7 +89,7 @@ class AnnotationPoint extends React.Component {
} }
render() { render() {
const {annotation, mode, dygraph} = this.props const {annotation, mode, dygraph, staticLegendHeight} = this.props
const {isDragging} = this.state const {isDragging} = this.state
const isEditing = mode === EDITING const isEditing = mode === EDITING
@ -100,11 +104,16 @@ class AnnotationPoint extends React.Component {
? 'annotation--click-area editing' ? 'annotation--click-area editing'
: 'annotation--click-area' : 'annotation--click-area'
const left = `${dygraph.toDomXCoord(annotation.startTime) + const markerStyles = {
DYGRAPH_CONTAINER_MARGIN}px` left: `${dygraph.toDomXCoord(annotation.startTime) +
DYGRAPH_CONTAINER_H_MARGIN}px`,
height: `calc(100% - ${staticLegendHeight +
DYGRAPH_CONTAINER_XLABEL_MARGIN +
DYGRAPH_CONTAINER_V_MARGIN * 2}px)`,
}
return ( return (
<div className={markerClass} style={{left}}> <div className={markerClass} style={markerStyles}>
<div <div
className={clickClass} className={clickClass}
draggable={true} draggable={true}
@ -127,12 +136,19 @@ class AnnotationPoint extends React.Component {
} }
} }
const {func, number, shape, string} = PropTypes
AnnotationPoint.defaultProps = {
staticLegendHeight: 0,
}
AnnotationPoint.propTypes = { AnnotationPoint.propTypes = {
annotation: schema.annotation.isRequired, annotation: schema.annotation.isRequired,
mode: PropTypes.string.isRequired, mode: string.isRequired,
dygraph: PropTypes.shape({}).isRequired, dygraph: shape({}).isRequired,
updateAnnotation: PropTypes.func.isRequired, updateAnnotation: func.isRequired,
updateAnnotationAsync: PropTypes.func.isRequired, updateAnnotationAsync: func.isRequired,
staticLegendHeight: number.isRequired,
} }
const mdtp = { const mdtp = {

View File

@ -1,7 +1,11 @@
import React, {PropTypes} from 'react' import React, {PropTypes} from 'react'
import {connect} from 'react-redux' import {connect} from 'react-redux'
import {DYGRAPH_CONTAINER_MARGIN} from 'shared/constants' import {
DYGRAPH_CONTAINER_H_MARGIN,
DYGRAPH_CONTAINER_V_MARGIN,
DYGRAPH_CONTAINER_XLABEL_MARGIN,
} from 'shared/constants'
import {ANNOTATION_MIN_DELTA, EDITING} from 'shared/annotations/helpers' import {ANNOTATION_MIN_DELTA, EDITING} from 'shared/annotations/helpers'
import * as schema from 'shared/schemas' import * as schema from 'shared/schemas'
import * as actions from 'shared/actions/annotations' import * as actions from 'shared/actions/annotations'
@ -92,7 +96,7 @@ class AnnotationSpan extends React.Component {
renderLeftMarker(startTime, dygraph) { renderLeftMarker(startTime, dygraph) {
const isEditing = this.props.mode === EDITING const isEditing = this.props.mode === EDITING
const {isDragging, isMouseOver} = this.state const {isDragging, isMouseOver} = this.state
const {annotation} = this.props const {annotation, staticLegendHeight} = this.props
const flagClass = isDragging const flagClass = isDragging
? 'annotation-span--left-flag dragging' ? 'annotation-span--left-flag dragging'
@ -108,10 +112,15 @@ class AnnotationSpan extends React.Component {
} }
const showTooltip = isDragging === 'left' || isMouseOver === 'left' const showTooltip = isDragging === 'left' || isMouseOver === 'left'
const left = dygraph.toDomXCoord(startTime) + DYGRAPH_CONTAINER_MARGIN const markerStyles = {
left: `${dygraph.toDomXCoord(startTime) + DYGRAPH_CONTAINER_H_MARGIN}px`,
height: `calc(100% - ${staticLegendHeight +
DYGRAPH_CONTAINER_XLABEL_MARGIN +
DYGRAPH_CONTAINER_V_MARGIN * 2}px)`,
}
return ( return (
<div className={markerClass} style={{left: `${left}px`}}> <div className={markerClass} style={markerStyles}>
{showTooltip && {showTooltip &&
<AnnotationTooltip <AnnotationTooltip
isEditing={isEditing} isEditing={isEditing}
@ -136,9 +145,8 @@ class AnnotationSpan extends React.Component {
renderRightMarker(endTime, dygraph) { renderRightMarker(endTime, dygraph) {
const isEditing = this.props.mode === EDITING const isEditing = this.props.mode === EDITING
const humanTime = `${new Date(+endTime)}`
const {isDragging, isMouseOver} = this.state const {isDragging, isMouseOver} = this.state
const {annotation} = this.props const {annotation, staticLegendHeight} = this.props
const flagClass = isDragging const flagClass = isDragging
? 'annotation-span--right-flag dragging' ? 'annotation-span--right-flag dragging'
@ -154,15 +162,15 @@ class AnnotationSpan extends React.Component {
} }
const showTooltip = isDragging === 'right' || isMouseOver === 'right' const showTooltip = isDragging === 'right' || isMouseOver === 'right'
const left = `${dygraph.toDomXCoord(endTime) + DYGRAPH_CONTAINER_MARGIN}px` const markerStyles = {
left: `${dygraph.toDomXCoord(endTime) + DYGRAPH_CONTAINER_H_MARGIN}px`,
height: `calc(100% - ${staticLegendHeight +
DYGRAPH_CONTAINER_XLABEL_MARGIN +
DYGRAPH_CONTAINER_V_MARGIN * 2}px)`,
}
return ( return (
<div <div className={markerClass} style={markerStyles}>
className={markerClass}
style={{left}}
data-time-ms={endTime}
data-time-local={humanTime}
>
{showTooltip && {showTooltip &&
<AnnotationTooltip <AnnotationTooltip
isEditing={isEditing} isEditing={isEditing}
@ -204,13 +212,19 @@ class AnnotationSpan extends React.Component {
} }
} }
const {func, number, shape, string} = PropTypes
AnnotationSpan.defaultProps = {
staticLegendHeight: 0,
}
AnnotationSpan.propTypes = { AnnotationSpan.propTypes = {
annotation: schema.annotation.isRequired, annotation: schema.annotation.isRequired,
mode: PropTypes.string.isRequired, mode: string.isRequired,
dygraph: PropTypes.shape({}).isRequired, dygraph: shape({}).isRequired,
staticLegendHeight: PropTypes.number.isRequired, staticLegendHeight: number.isRequired,
updateAnnotationAsync: PropTypes.func.isRequired, updateAnnotationAsync: func.isRequired,
updateAnnotation: PropTypes.func.isRequired, updateAnnotation: func.isRequired,
} }
const mdtp = { const mdtp = {

View File

@ -1,6 +1,10 @@
import React, {PropTypes} from 'react' import React, {PropTypes} from 'react'
import {DYGRAPH_CONTAINER_MARGIN} from 'shared/constants' import {
DYGRAPH_CONTAINER_H_MARGIN,
DYGRAPH_CONTAINER_V_MARGIN,
DYGRAPH_CONTAINER_XLABEL_MARGIN,
} from 'shared/constants'
import * as schema from 'shared/schemas' import * as schema from 'shared/schemas'
const windowDimensions = (anno, dygraph, staticLegendHeight) => { const windowDimensions = (anno, dygraph, staticLegendHeight) => {
@ -14,10 +18,12 @@ const windowDimensions = (anno, dygraph, staticLegendHeight) => {
const windowWidth = Math.abs(windowEndXCoord - windowStartXCoord) const windowWidth = Math.abs(windowEndXCoord - windowStartXCoord)
const windowLeftXCoord = const windowLeftXCoord =
Math.min(windowStartXCoord, windowEndXCoord) + DYGRAPH_CONTAINER_MARGIN Math.min(windowStartXCoord, windowEndXCoord) + DYGRAPH_CONTAINER_H_MARGIN
const height = staticLegendHeight const height = staticLegendHeight
? `calc(100% - ${staticLegendHeight + 36}px)` ? `calc(100% - ${staticLegendHeight +
DYGRAPH_CONTAINER_XLABEL_MARGIN +
DYGRAPH_CONTAINER_V_MARGIN * 2}px)`
: 'calc(100% - 36px)' : 'calc(100% - 36px)'
return { return {

View File

@ -8,6 +8,8 @@ import AnnotationWindow from 'shared/components/AnnotationWindow'
import * as schema from 'shared/schemas' import * as schema from 'shared/schemas'
import * as actions from 'shared/actions/annotations' import * as actions from 'shared/actions/annotations'
import {DYGRAPH_CONTAINER_XLABEL_MARGIN} from 'shared/constants'
class NewAnnotation extends Component { class NewAnnotation extends Component {
state = { state = {
isMouseOver: false, isMouseOver: false,
@ -123,6 +125,8 @@ class NewAnnotation extends Component {
const crosshairOne = Math.max(-1000, dygraph.toDomXCoord(startTime)) const crosshairOne = Math.max(-1000, dygraph.toDomXCoord(startTime))
const crosshairTwo = dygraph.toDomXCoord(endTime) const crosshairTwo = dygraph.toDomXCoord(endTime)
const crosshairHeight = `calc(100% - ${staticLegendHeight +
DYGRAPH_CONTAINER_XLABEL_MARGIN}px)`
const isDragging = startTime !== endTime const isDragging = startTime !== endTime
const flagOneClass = const flagOneClass =
@ -158,7 +162,7 @@ class NewAnnotation extends Component {
{isDragging && {isDragging &&
<div <div
className="new-annotation--crosshair" className="new-annotation--crosshair"
style={{left: crosshairTwo}} style={{left: crosshairTwo, height: crosshairHeight}}
> >
{isMouseOver && {isMouseOver &&
isDragging && isDragging &&
@ -167,7 +171,7 @@ class NewAnnotation extends Component {
</div>} </div>}
<div <div
className="new-annotation--crosshair" className="new-annotation--crosshair"
style={{left: crosshairOne}} style={{left: crosshairOne, height: crosshairHeight}}
> >
{isMouseOver && {isMouseOver &&
!isDragging && !isDragging &&

View File

@ -55,6 +55,7 @@ const RefreshingGraph = ({
cellHeight={cellHeight} cellHeight={cellHeight}
prefix={prefix} prefix={prefix}
suffix={suffix} suffix={suffix}
inView={inView}
/> />
) )
} }
@ -73,6 +74,7 @@ const RefreshingGraph = ({
cellID={cellID} cellID={cellID}
prefix={prefix} prefix={prefix}
suffix={suffix} suffix={suffix}
inView={inView}
/> />
) )
} }

View File

@ -415,7 +415,9 @@ export const PAGE_CONTAINER_MARGIN = 30 // TODO: get this dynamically to ensure
export const LAYOUT_MARGIN = 4 export const LAYOUT_MARGIN = 4
export const DASHBOARD_LAYOUT_ROW_HEIGHT = 83.5 export const DASHBOARD_LAYOUT_ROW_HEIGHT = 83.5
export const DYGRAPH_CONTAINER_MARGIN = 16 export const DYGRAPH_CONTAINER_H_MARGIN = 16
export const DYGRAPH_CONTAINER_V_MARGIN = 8
export const DYGRAPH_CONTAINER_XLABEL_MARGIN = 20
export const DEFAULT_SOURCE = { export const DEFAULT_SOURCE = {
url: 'http://localhost:8086', url: 'http://localhost:8086',