diff --git a/ui/src/dashboards/components/Dashboard.js b/ui/src/dashboards/components/Dashboard.js
index c3428829b7..4aae1b459e 100644
--- a/ui/src/dashboards/components/Dashboard.js
+++ b/ui/src/dashboards/components/Dashboard.js
@@ -16,7 +16,6 @@ const Dashboard = ({
autoRefresh,
manualRefresh,
onDeleteCell,
- synchronizer,
onPositionChange,
inPresentationMode,
onOpenTemplateManager,
@@ -26,6 +25,8 @@ const Dashboard = ({
showTemplateControlBar,
setScrollTop,
inView,
+ onSetHoverTime,
+ hoverTime,
}) => {
const cells = dashboard.cells.map(cell => {
const dashboardCell = {
@@ -66,7 +67,8 @@ const Dashboard = ({
timeRange={timeRange}
autoRefresh={autoRefresh}
manualRefresh={manualRefresh}
- synchronizer={synchronizer}
+ hoverTime={hoverTime}
+ onSetHoverTime={onSetHoverTime}
onDeleteCell={onDeleteCell}
onPositionChange={onPositionChange}
templates={templatesIncludingDashTime}
@@ -112,7 +114,8 @@ Dashboard.propTypes = {
onPositionChange: func,
onDeleteCell: func,
onSummonOverlayTechnologies: func,
- synchronizer: func,
+ hoverTime: string,
+ onSetHoverTime: func,
source: shape({
links: shape({
proxy: string,
diff --git a/ui/src/dashboards/containers/DashboardPage.js b/ui/src/dashboards/containers/DashboardPage.js
index 1eeec2672f..e60434a0d0 100644
--- a/ui/src/dashboards/containers/DashboardPage.js
+++ b/ui/src/dashboards/containers/DashboardPage.js
@@ -5,7 +5,6 @@ import {withRouter} from 'react-router'
import {bindActionCreators} from 'redux'
import _ from 'lodash'
-import Dygraph from 'src/external/dygraph'
import {isUserAuthorized, EDITOR_ROLE} from 'src/auth/Authorized'
@@ -19,6 +18,7 @@ import ManualRefresh from 'src/shared/components/ManualRefresh'
import {errorThrown as errorThrownAction} from 'shared/actions/errors'
import {publishNotification} from 'shared/actions/notifications'
import idNormalizer, {TYPE_ID} from 'src/normalizers/id'
+import {NULL_HOVER_TIME} from 'src/shared/constants/tableGraph'
import * as dashboardActionCreators from 'src/dashboards/actions'
import * as annotationActions from 'shared/actions/annotations'
@@ -54,6 +54,7 @@ class DashboardPage extends Component {
zoomedTimeRange: {zoomedLower: null, zoomedUpper: null},
scrollTop: 0,
windowHeight: window.innerHeight,
+ hoverTime: NULL_HOVER_TIME,
}
}
@@ -238,30 +239,8 @@ class DashboardPage extends Component {
this.props.errorThrown(error)
}
- synchronizer = dygraph => {
- const dygraphs = [...this.dygraphs, dygraph].filter(d => d.graphDiv)
- const {dashboards, params: {dashboardID}} = this.props
-
- const dashboard = dashboards.find(
- d => d.id === idNormalizer(TYPE_ID, dashboardID)
- )
-
- // Get only the graphs that can sync the hover line
- const graphsToSync = dashboard.cells.filter(c => c.type !== 'single-stat')
-
- if (
- dashboard &&
- dygraphs.length === graphsToSync.length &&
- dygraphs.length > 1
- ) {
- Dygraph.synchronize(dygraphs, {
- selection: true,
- zoom: false,
- range: false,
- })
- }
-
- this.dygraphs = dygraphs
+ handleSetHoverTime = hoverTime => {
+ this.setState({hoverTime})
}
handleToggleTempVarControls = () => {
@@ -277,9 +256,8 @@ class DashboardPage extends Component {
}
render() {
- const {zoomedTimeRange} = this.state
+ const {zoomedTimeRange, hoverTime} = this.state
const {zoomedLower, zoomedUpper} = zoomedTimeRange
-
const {
source,
sources,
@@ -440,7 +418,8 @@ class DashboardPage extends Component {
manualRefresh={manualRefresh}
onZoom={this.handleZoomedTimeRange}
onAddCell={this.handleAddCell}
- synchronizer={this.synchronizer}
+ hoverTime={hoverTime}
+ onSetHoverTime={this.handleSetHoverTime}
inPresentationMode={inPresentationMode}
onPositionChange={this.handleUpdatePosition}
onSelectTemplate={this.handleSelectTemplate}
diff --git a/ui/src/dashboards/graphics/graph.js b/ui/src/dashboards/graphics/graph.js
index 9658606c87..4038517bb8 100644
--- a/ui/src/dashboards/graphics/graph.js
+++ b/ui/src/dashboards/graphics/graph.js
@@ -526,9 +526,9 @@ export const GRAPH_TYPES = [
graphic: GRAPH_SVGS['line-stepplot'],
},
{
- type: 'single-stat',
- menuOption: 'Single Stat',
- graphic: GRAPH_SVGS['single-stat'],
+ type: 'bar',
+ menuOption: 'Bar Graph',
+ graphic: GRAPH_SVGS.bar,
},
{
type: 'line-plus-single-stat',
@@ -536,9 +536,9 @@ export const GRAPH_TYPES = [
graphic: GRAPH_SVGS['line-plus-single-stat'],
},
{
- type: 'bar',
- menuOption: 'Bar Graph',
- graphic: GRAPH_SVGS.bar,
+ type: 'single-stat',
+ menuOption: 'Single Stat',
+ graphic: GRAPH_SVGS['single-stat'],
},
{
type: 'gauge',
diff --git a/ui/src/hosts/containers/HostPage.js b/ui/src/hosts/containers/HostPage.js
index f513305b20..9af0f055ab 100644
--- a/ui/src/hosts/containers/HostPage.js
+++ b/ui/src/hosts/containers/HostPage.js
@@ -5,8 +5,6 @@ import {bindActionCreators} from 'redux'
import _ from 'lodash'
import classnames from 'classnames'
-import Dygraph from 'src/external/dygraph'
-
import LayoutRenderer from 'shared/components/LayoutRenderer'
import DashboardHeader from 'src/dashboards/components/DashboardHeader'
import FancyScrollbar from 'shared/components/FancyScrollbar'
@@ -84,22 +82,6 @@ class HostPage extends Component {
}
}
- synchronizer = dygraph => {
- const dygraphs = [...this.state.dygraphs, dygraph].filter(d => d.graphDiv)
- const numGraphs = this.state.layouts.reduce((acc, {cells}) => {
- return acc + cells.length
- }, 0)
-
- if (dygraphs.length === numGraphs) {
- Dygraph.synchronize(dygraphs, {
- selection: true,
- zoom: false,
- range: false,
- })
- }
- this.setState({dygraphs})
- }
-
renderLayouts = layouts => {
const {timeRange} = this.state
const {source, autoRefresh, manualRefresh} = this.props
@@ -157,7 +139,6 @@ class HostPage extends Component {
autoRefresh={autoRefresh}
manualRefresh={manualRefresh}
host={this.props.params.hostID}
- synchronizer={this.synchronizer}
/>
)
}
diff --git a/ui/src/shared/components/Crosshair.js b/ui/src/shared/components/Crosshair.js
new file mode 100644
index 0000000000..a111dd35df
--- /dev/null
+++ b/ui/src/shared/components/Crosshair.js
@@ -0,0 +1,43 @@
+import React, {PropTypes, Component} from 'react'
+import {DYGRAPH_CONTAINER_XLABEL_MARGIN} from 'shared/constants'
+import {NULL_HOVER_TIME} from 'shared/constants/tableGraph'
+
+import classnames from 'classnames'
+
+class Crosshair extends Component {
+ render() {
+ const {dygraph, staticLegendHeight, hoverTime} = this.props
+ const crosshairLeft = Math.round(
+ Math.max(-1000, dygraph.toDomXCoord(hoverTime)) || -1000 + 1
+ )
+ const crosshairHeight = `calc(100% - ${staticLegendHeight +
+ DYGRAPH_CONTAINER_XLABEL_MARGIN}px)`
+
+ const crosshairHidden = hoverTime === NULL_HOVER_TIME
+
+ return (
+
+ )
+ }
+}
+
+const {number, shape, string} = PropTypes
+
+Crosshair.propTypes = {
+ dygraph: shape({}),
+ staticLegendHeight: number,
+ hoverTime: string,
+}
+
+export default Crosshair
diff --git a/ui/src/shared/components/Dygraph.js b/ui/src/shared/components/Dygraph.js
index eff78785ba..da9a658986 100644
--- a/ui/src/shared/components/Dygraph.js
+++ b/ui/src/shared/components/Dygraph.js
@@ -10,12 +10,13 @@ import Dygraphs from 'src/external/dygraph'
import DygraphLegend from 'src/shared/components/DygraphLegend'
import StaticLegend from 'src/shared/components/StaticLegend'
import Annotations from 'src/shared/components/Annotations'
+import Crosshair from 'src/shared/components/Crosshair'
import getRange, {getStackedRange} from 'shared/parsing/getRangeForDygraph'
import {DISPLAY_OPTIONS} from 'src/dashboards/constants'
import {buildDefaultYLabel} from 'shared/presenters'
import {numberValueFormatter} from 'src/utils/formatting'
-
+import {NULL_HOVER_TIME} from 'src/shared/constants/tableGraph'
import {
OPTIONS,
LINE_COLORS,
@@ -31,9 +32,9 @@ class Dygraph extends Component {
constructor(props) {
super(props)
this.state = {
- isSynced: false,
isHidden: true,
staticLegendHeight: null,
+ isNotHovering: true,
}
}
@@ -53,7 +54,6 @@ class Dygraph extends Component {
logscale: y.scale === LOG,
colors: this.getLineColors(),
series: this.hashColorDygraphSeries(),
- unhighlightCallback: this.unhighlightCallback,
plugins: [new Dygraphs.Plugins.Crosshair({direction: 'vertical'})],
axes: {
y: {
@@ -92,11 +92,6 @@ class Dygraph extends Component {
const {w} = this.dygraph.getArea()
this.props.setResolution(w)
-
- // Simple opt-out for now, if a graph should not be synced
- if (this.props.synchronizer) {
- this.sync()
- }
}
componentWillUnmount() {
@@ -194,6 +189,30 @@ class Dygraph extends Component {
onZoom(this.formatTimeRange(lower), this.formatTimeRange(upper))
}
+ eventToTimestamp = ({pageX: pxBetweenMouseAndPage}) => {
+ const {left: pxBetweenGraphAndPage} = this.graphRef.getBoundingClientRect()
+ const graphXCoordinate = pxBetweenMouseAndPage - pxBetweenGraphAndPage
+ const timestamp = this.dygraph.toDataXCoord(graphXCoordinate)
+ const [xRangeStart] = this.dygraph.xAxisRange()
+ const clamped = Math.max(xRangeStart, timestamp)
+ return `${clamped}`
+ }
+
+ handleMouseMove = e => {
+ if (this.props.onSetHoverTime) {
+ const newTime = this.eventToTimestamp(e)
+ this.props.onSetHoverTime(newTime)
+ }
+ this.setState({isNotHovering: false})
+ }
+
+ handleMouseOut = () => {
+ if (this.props.onSetHoverTime) {
+ this.props.onSetHoverTime(NULL_HOVER_TIME)
+ }
+ this.setState({isNotHovering: true})
+ }
+
hashColorDygraphSeries = () => {
const {dygraphSeries} = this.props
const colors = this.getLineColors()
@@ -209,13 +228,6 @@ class Dygraph extends Component {
return hashColorDygraphSeries
}
- sync = () => {
- if (!this.state.isSynced) {
- this.props.synchronizer(this.dygraph)
- this.setState({isSynced: true})
- }
- }
-
handleHideLegend = e => {
const {top, bottom, left, right} = this.graphRef.getBoundingClientRect()
@@ -302,8 +314,7 @@ class Dygraph extends Component {
render() {
const {isHidden, staticLegendHeight} = this.state
- const {staticLegend, children} = this.props
-
+ const {staticLegend, children, hoverTime} = this.props
const nestedGraph = (children && children.length && children[0]) || children
let dygraphStyle = {...this.props.containerStyle, zIndex: '2'}
if (staticLegend) {
@@ -319,18 +330,25 @@ class Dygraph extends Component {
return (
{this.dygraph &&
-
}
- {this.dygraph &&
-
}
+
+
+
+ {this.state.isNotHovering &&
+
}
+
}
{
this.graphRef = r
@@ -338,6 +356,8 @@ class Dygraph extends Component {
}}
className="dygraph-child-container"
style={dygraphStyle}
+ onMouseMove={this.handleMouseMove}
+ onMouseOut={this.handleMouseOut}
/>
{staticLegend &&
@@ -85,7 +86,8 @@ const Layout = (
timeRange={timeRange}
templates={templates}
autoRefresh={autoRefresh}
- synchronizer={synchronizer}
+ hoverTime={hoverTime}
+ onSetHoverTime={onSetHoverTime}
manualRefresh={manualRefresh}
onStopAddAnnotation={onStopAddAnnotation}
grabDataForDownload={grabDataForDownload}
@@ -149,7 +151,8 @@ const propTypes = {
onEditCell: func,
onDeleteCell: func,
onSummonOverlayTechnologies: func,
- synchronizer: func,
+ hoverTime: string,
+ onSetHoverTime: func,
isStatusPage: bool,
isEditable: bool,
onCancelEditCell: func,
diff --git a/ui/src/shared/components/LayoutRenderer.js b/ui/src/shared/components/LayoutRenderer.js
index f853071e41..17d3509e47 100644
--- a/ui/src/shared/components/LayoutRenderer.js
+++ b/ui/src/shared/components/LayoutRenderer.js
@@ -72,9 +72,10 @@ class LayoutRenderer extends Component {
autoRefresh,
manualRefresh,
onDeleteCell,
- synchronizer,
onCancelEditCell,
onSummonOverlayTechnologies,
+ hoverTime,
+ onSetHoverTime,
} = this.props
const {rowHeight, resizeCoords} = this.state
@@ -129,7 +130,8 @@ class LayoutRenderer extends Component {
autoRefresh={autoRefresh}
resizeCoords={resizeCoords}
onDeleteCell={onDeleteCell}
- synchronizer={synchronizer}
+ hoverTime={hoverTime}
+ onSetHoverTime={onSetHoverTime}
manualRefresh={manualRefresh}
onCancelEditCell={onCancelEditCell}
onStopAddAnnotation={this.handleStopAddAnnotation}
@@ -184,7 +186,8 @@ LayoutRenderer.propTypes = {
onEditCell: func,
onDeleteCell: func,
onSummonOverlayTechnologies: func,
- synchronizer: func,
+ hoverTime: string,
+ onSetHoverTime: func,
isStatusPage: bool,
isEditable: bool,
onCancelEditCell: func,
diff --git a/ui/src/shared/components/LineGraph.js b/ui/src/shared/components/LineGraph.js
index 7c73e8ae76..8238da7abd 100644
--- a/ui/src/shared/components/LineGraph.js
+++ b/ui/src/shared/components/LineGraph.js
@@ -49,7 +49,6 @@ class LineGraph extends Component {
ruleValues,
isBarGraph,
resizeCoords,
- synchronizer,
isRefreshing,
setResolution,
isGraphFilled,
@@ -59,6 +58,8 @@ class LineGraph extends Component {
underlayCallback,
overrideLineColors,
isFetchingInitially,
+ hoverTime,
+ onSetHoverTime,
} = this.props
const {labels, timeSeries, dygraphSeries} = this._timeSeries
@@ -111,7 +112,8 @@ class LineGraph extends Component {
isBarGraph={isBarGraph}
timeSeries={timeSeries}
ruleValues={ruleValues}
- synchronizer={synchronizer}
+ hoverTime={hoverTime}
+ onSetHoverTime={onSetHoverTime}
resizeCoords={resizeCoords}
dygraphSeries={dygraphSeries}
setResolution={setResolution}
@@ -186,7 +188,8 @@ LineGraph.propTypes = {
lower: string.isRequired,
}),
isInDataExplorer: bool,
- synchronizer: func,
+ hoverTime: string,
+ onSetHoverTime: func,
setResolution: func,
cellHeight: number,
cell: shape(),
diff --git a/ui/src/shared/components/RefreshingGraph.js b/ui/src/shared/components/RefreshingGraph.js
index 7a1544c404..a406327c72 100644
--- a/ui/src/shared/components/RefreshingGraph.js
+++ b/ui/src/shared/components/RefreshingGraph.js
@@ -29,10 +29,11 @@ const RefreshingGraph = ({
resizerTopHeight,
staticLegend,
manualRefresh, // when changed, re-mounts the component
- synchronizer,
resizeCoords,
editQueryStatus,
grabDataForDownload,
+ hoverTime,
+ onSetHoverTime,
}) => {
const prefix = (axes && axes.y.prefix) || ''
const suffix = (axes && axes.y.suffix) || ''
@@ -94,8 +95,8 @@ const RefreshingGraph = ({
resizerTopHeight={resizerTopHeight}
resizeCoords={resizeCoords}
cellID={cellID}
- // prefix={prefix}
- // suffix={suffix}
+ hoverTime={hoverTime}
+ onSetHoverTime={onSetHoverTime}
inView={inView}
/>
)
@@ -118,7 +119,8 @@ const RefreshingGraph = ({
timeRange={timeRange}
autoRefresh={autoRefresh}
isBarGraph={type === 'bar'}
- synchronizer={synchronizer}
+ hoverTime={hoverTime}
+ onSetHoverTime={onSetHoverTime}
resizeCoords={resizeCoords}
staticLegend={staticLegend}
displayOptions={displayOptions}
@@ -138,7 +140,8 @@ RefreshingGraph.propTypes = {
autoRefresh: number.isRequired,
manualRefresh: number,
templates: arrayOf(shape()),
- synchronizer: func,
+ hoverTime: string,
+ onSetHoverTime: func,
type: string.isRequired,
cellHeight: number,
resizerTopHeight: number,
diff --git a/ui/src/shared/components/TableGraph.js b/ui/src/shared/components/TableGraph.js
index 0d9124f276..db970615f3 100644
--- a/ui/src/shared/components/TableGraph.js
+++ b/ui/src/shared/components/TableGraph.js
@@ -2,32 +2,72 @@ import React, {Component} from 'react'
import PropTypes from 'prop-types'
import _ from 'lodash'
import classnames from 'classnames'
-import {timeSeriesToTable} from 'src/utils/timeSeriesToDygraph'
+
+import {timeSeriesToTableGraph} from 'src/utils/timeSeriesToDygraph'
+import {
+ NULL_COLUMN_INDEX,
+ NULL_ROW_INDEX,
+ NULL_HOVER_TIME,
+} from 'src/shared/constants/tableGraph'
+
import {MultiGrid} from 'react-virtualized'
class TableGraph extends Component {
+ constructor(props) {
+ super(props)
+ this.state = {
+ hoveredColumnIndex: NULL_COLUMN_INDEX,
+ hoveredRowIndex: NULL_ROW_INDEX,
+ }
+ }
+
componentWillMount() {
this._labels = []
this._data = [[]]
}
componentWillUpdate(nextProps) {
- // TODO: determine if in dataExplorer
- const {labels, data} = timeSeriesToTable(nextProps.data)
+ const {labels, data} = timeSeriesToTableGraph(nextProps.data)
this._labels = labels
this._data = data
}
- cellRenderer = ({columnIndex, key, rowIndex, style}) => {
+ handleHover = (columnIndex, rowIndex) => () => {
+ if (this.props.onSetHoverTime) {
+ this.props.onSetHoverTime(this._data[rowIndex][0].toString())
+ this.setState({
+ hoveredColumnIndex: columnIndex,
+ hoveredRowIndex: rowIndex,
+ })
+ }
+ }
+
+ handleMouseOut = () => {
+ if (this.props.onSetHoverTime) {
+ this.props.onSetHoverTime(NULL_HOVER_TIME)
+ this.setState({
+ hoveredColumnIndex: NULL_COLUMN_INDEX,
+ hoveredRowIndex: NULL_ROW_INDEX,
+ })
+ }
+ }
+
+ cellRenderer = ({columnIndex, rowIndex, key, style, parent}) => {
const data = this._data
+ const {hoveredColumnIndex, hoveredRowIndex} = this.state
+
const columnCount = _.get(data, ['0', 'length'], 0)
const rowCount = data.length
-
const isFixedRow = rowIndex === 0 && columnIndex > 0
const isFixedColumn = rowIndex > 0 && columnIndex === 0
const isFixedCorner = rowIndex === 0 && columnIndex === 0
const isLastRow = rowIndex === rowCount - 1
const isLastColumn = columnIndex === columnCount - 1
+ const isHighlighted =
+ rowIndex === parent.props.scrollToRow ||
+ (rowIndex === hoveredRowIndex && hoveredRowIndex !== 0) ||
+ (columnIndex === hoveredColumnIndex && hoveredColumnIndex !== 0)
+ const dataIsNumerical = _.isNumber([rowIndex][columnIndex])
const cellClass = classnames('table-graph-cell', {
'table-graph-cell__fixed-row': isFixedRow,
@@ -35,16 +75,25 @@ class TableGraph extends Component {
'table-graph-cell__fixed-corner': isFixedCorner,
'table-graph-cell__last-row': isLastRow,
'table-graph-cell__last-column': isLastColumn,
+ 'table-graph-cell__highlight': isHighlighted,
+ 'table-graph-cell__numerical': dataIsNumerical,
})
return (
-
- {data[rowIndex][columnIndex]}
+
+ {`${data[rowIndex][columnIndex]}`}
)
}
render() {
+ const {hoveredColumnIndex, hoveredRowIndex} = this.state
+ const {hoverTime} = this.props
const data = this._data
const columnCount = _.get(data, ['0', 'length'], 0)
const rowCount = data.length
@@ -53,35 +102,49 @@ class TableGraph extends Component {
const tableWidth = this.gridContainer ? this.gridContainer.clientWidth : 0
const tableHeight = this.gridContainer ? this.gridContainer.clientHeight : 0
+ const hoverTimeRow =
+ data.length > 1 && hoverTime > 0
+ ? data.findIndex(
+ row => row[0] && _.isNumber(row[0]) && row[0] >= hoverTime
+ )
+ : undefined
+
return (
(this.gridContainer = gridContainer)}
+ onMouseOut={this.handleMouseOut}
>
{data.length > 1 &&
}
)
}
}
-const {arrayOf, number, shape} = PropTypes
+const {arrayOf, number, shape, string, func} = PropTypes
TableGraph.propTypes = {
cellHeight: number,
data: arrayOf(shape()),
+ hoverTime: string,
+ onSetHoverTime: func,
}
export default TableGraph
diff --git a/ui/src/shared/constants/tableGraph.js b/ui/src/shared/constants/tableGraph.js
new file mode 100644
index 0000000000..717d015751
--- /dev/null
+++ b/ui/src/shared/constants/tableGraph.js
@@ -0,0 +1,4 @@
+export const NULL_COLUMN_INDEX = -1
+export const NULL_ROW_INDEX = -1
+
+export const NULL_HOVER_TIME = '0'
diff --git a/ui/src/style/chronograf.scss b/ui/src/style/chronograf.scss
index 0efcca69bd..ccb2c7f1b4 100644
--- a/ui/src/style/chronograf.scss
+++ b/ui/src/style/chronograf.scss
@@ -33,6 +33,7 @@
// Components
@import 'components/annotations';
+@import 'components/crosshairs';
@import 'components/ceo-display-options';
@import 'components/confirm-button';
@import 'components/confirm-buttons';
diff --git a/ui/src/style/components/crosshairs.scss b/ui/src/style/components/crosshairs.scss
new file mode 100644
index 0000000000..a6d488ecca
--- /dev/null
+++ b/ui/src/style/components/crosshairs.scss
@@ -0,0 +1,29 @@
+/*
+ Crosshairs
+ ------------------------------------------------------------------------------
+*/
+
+%crosshair-styles {
+ position: absolute;
+ cursor: pointer;
+}
+
+.crosshair-container {
+ @extend %crosshair-styles;
+ z-index: 0;
+ top: 8px;
+ width: calc(100% - 32px);
+ height: calc(100% - 16px);
+}
+
+.crosshair {
+ @extend %crosshair-styles;
+ top: 0;
+ height: calc(100% - 20px);
+ width: 0.5px;
+ background-color: $g14-chromium;
+}
+
+.crosshair.hidden {
+ visibility: hidden;
+}
diff --git a/ui/src/style/components/table-graph.scss b/ui/src/style/components/table-graph.scss
index aba63236f7..a909197a12 100644
--- a/ui/src/style/components/table-graph.scss
+++ b/ui/src/style/components/table-graph.scss
@@ -4,45 +4,104 @@
*/
.table-graph-container {
- position: absolute;
- width: calc(100% - 32px);
- height: calc(100% - 16px);
- top: 8px;
- left: 16px;
- border: 2px solid $g5-pepper;
- border-radius: 3px;
- overflow: hidden;
+ position: absolute;
+ width: calc(100% - 32px);
+ height: calc(100% - 16px);
+ top: 8px;
+ left: 16px;
+ border: 2px solid $g5-pepper;
+ border-radius: 3px;
+ overflow: hidden;
}
.table-graph-cell {
- line-height: 30px;
- padding: 0 6px;
- font-size: 13px;
- color: $g13-mist;
- border: 1px solid $g5-pepper;
+ line-height: 28px; // Cell height - 2x border width
+ padding: 0 6px;
+ font-size: 13px;
+ color: $g12-forge;
+ border: 1px solid $g5-pepper;
- &__fixed-row,
- &__fixed-column {
- font-weight: 700;
- color: $g16-pearl;
- background-color: $g4-onyx;
- }
- &__fixed-row {
- border-top: 0;
- }
- &__fixed-column {
- border-left: 0;
- }
- &__fixed-corner {
- border-top: 0;
- border-left: 0;
- color: $g18-cloud;
- background-color: $g5-pepper;
- }
- &__last-row {
- border-bottom: 0;
- }
- &__last-column {
- border-right: 0;
- }
+ // Blue Highlight
+ &:after {
+ content: '';
+ position: absolute;
+ top: -1px;
+ left: -1px;
+ width: calc(100% + 2px);
+ height: calc(100% + 2px);
+ z-index: 5;
+ border: 2px solid $c-pool;
+ border-radius: 3px;
+ visibility: hidden;
+ box-sizing: border-box;
+ }
+
+ &__numerical {
+ font-family: $code-font;
+ }
+ &__fixed-row,
+ &__fixed-column {
+ font-weight: 700;
+ color: $g14-chromium;
+ background-color: $g4-onyx;
+ }
+ &__fixed-row {
+ border-top: 0;
+ }
+ &__fixed-column {
+ border-left: 0;
+ }
+ &__fixed-corner {
+ border-top: 0;
+ border-left: 0;
+ color: $g18-cloud;
+ background-color: $g5-pepper;
+ }
+ &__last-row {
+ border-bottom: 0;
+ }
+ &__last-column {
+ border-right: 0;
+ }
+ &__highlight {
+ background-color: $g6-smoke;
+ color: $g16-pearl;
+ }
+ &__highlight:not(.table-graph-cell__fixed-row):not(.table-graph-cell__fixed-column):not(.table-graph-cell__fixed-corner) {
+ color: $g14-chromium;
+ background-color: $g6-smoke;
+ }
+ &:hover:not(.table-graph-cell__fixed-row):not(.table-graph-cell__fixed-column):not(.table-graph-cell__fixed-corner) {
+ color: $g20-white;
+ background-color: $g0-obsidian;
+ &:after {visibility: visible;}
+ }
+}
+
+.ReactVirtualized__Grid {
+ &:focus {
+ outline: none;
+ }
+ &::-webkit-scrollbar {
+ width: 0;
+
+ &-button {
+ background-color: #f00;
+ }
+ &-track {
+ background-color: #f00;
+ }
+ &-track-piece {
+ background-color: #f00;
+ }
+ &-thumb {
+ background-color: #fff;
+ }
+ &-corner {
+ background-color: #f00;
+ }
+ }
+ &::-webkit-resizer {
+ background-color: #f00;
+ }
}
diff --git a/ui/src/utils/timeSeriesToDygraph.js b/ui/src/utils/timeSeriesToDygraph.js
index 243a6bd330..902667f45a 100644
--- a/ui/src/utils/timeSeriesToDygraph.js
+++ b/ui/src/utils/timeSeriesToDygraph.js
@@ -16,7 +16,7 @@ const cells = {
responseIndex: new Array(DEFAULT_SIZE),
}
-export default function timeSeriesToDygraph(raw = [], isInDataExplorer) {
+const timeSeriesTransform = (raw = []) => {
// collect results from each influx response
const results = reduce(
raw,
@@ -139,6 +139,15 @@ export default function timeSeriesToDygraph(raw = [], isInDataExplorer) {
}
const sortedTimeSeries = _.sortBy(timeSeries, 'time')
+ return {
+ sortedLabels,
+ sortedTimeSeries,
+ }
+}
+
+export const timeSeriesToDygraph = (raw = [], isInDataExplorer) => {
+ const {sortedLabels, sortedTimeSeries} = timeSeriesTransform(raw)
+
const dygraphSeries = reduce(
sortedLabels,
(acc, {label, responseIndex}) => {
@@ -147,7 +156,6 @@ export default function timeSeriesToDygraph(raw = [], isInDataExplorer) {
axis: responseIndex === 0 ? 'y' : 'y2',
}
}
-
return acc
},
{}
@@ -163,10 +171,17 @@ export default function timeSeriesToDygraph(raw = [], isInDataExplorer) {
}
}
-export const timeSeriesToTable = data => {
- const {labels, timeSeries} = timeSeriesToDygraph(data, false)
- const tableData = timeSeries.length
- ? timeSeries.map(row => row.map(cell => (cell ? cell.toString() : 'null')))
- : [[]]
- return {labels, data: [labels, ...tableData]}
+export const timeSeriesToTableGraph = raw => {
+ const {sortedLabels, sortedTimeSeries} = timeSeriesTransform(raw)
+
+ const labels = ['time', ...map(sortedLabels, ({label}) => label)]
+
+ const tableData = map(sortedTimeSeries, ({time, values}) => [time, ...values])
+
+ return {
+ labels,
+ data: tableData.length ? [labels, ...tableData] : [[]],
+ }
}
+
+export default timeSeriesToDygraph