Merge pull request #2940 from influxdata/feature/table-graph-cell-hover

TableGraph- hover time and crosshairs
pull/10616/head
Deniz Kusefoglu 2018-03-13 10:46:20 -07:00 committed by GitHub
commit 2d37fe09ba
16 changed files with 367 additions and 157 deletions

View File

@ -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,

View File

@ -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}

View File

@ -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',

View File

@ -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}
/>
)
}

View File

@ -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 (
<div className="crosshair-container">
<div
className={classnames('crosshair', {
hidden: crosshairHidden,
})}
style={{
left: crosshairLeft,
height: crosshairHeight,
zIndex: 1999,
}}
/>
</div>
)
}
}
const {number, shape, string} = PropTypes
Crosshair.propTypes = {
dygraph: shape({}),
staticLegendHeight: number,
hoverTime: string,
}
export default Crosshair

View File

@ -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 (
<div className="dygraph-child" onMouseLeave={this.deselectCrosshair}>
{this.dygraph &&
<Annotations
dygraph={this.dygraph}
annotationsRef={this.handleAnnotationsRef}
staticLegendHeight={staticLegendHeight}
/>}
{this.dygraph &&
<DygraphLegend
isHidden={isHidden}
dygraph={this.dygraph}
onHide={this.handleHideLegend}
onShow={this.handleShowLegend}
/>}
<div className="dygraph-addons">
<Annotations
dygraph={this.dygraph}
annotationsRef={this.handleAnnotationsRef}
staticLegendHeight={staticLegendHeight}
/>
<DygraphLegend
isHidden={isHidden}
dygraph={this.dygraph}
onHide={this.handleHideLegend}
onShow={this.handleShowLegend}
/>
{this.state.isNotHovering &&
<Crosshair
dygraph={this.dygraph}
staticLegendHeight={staticLegendHeight}
hoverTime={hoverTime}
/>}
</div>}
<div
ref={r => {
this.graphRef = r
@ -338,6 +356,8 @@ class Dygraph extends Component {
}}
className="dygraph-child-container"
style={dygraphStyle}
onMouseMove={this.handleMouseMove}
onMouseOut={this.handleMouseOut}
/>
{staticLegend &&
<StaticLegend
@ -407,7 +427,8 @@ Dygraph.propTypes = {
timeRange: shape({
lower: string.isRequired,
}),
synchronizer: func,
hoverTime: string,
onSetHoverTime: func,
setResolution: func,
dygraphRef: func,
onZoom: func,

View File

@ -53,12 +53,13 @@ const Layout = (
autoRefresh,
manualRefresh,
onDeleteCell,
synchronizer,
resizeCoords,
onCancelEditCell,
onStopAddAnnotation,
onSummonOverlayTechnologies,
grabDataForDownload,
hoverTime,
onSetHoverTime,
},
{source: defaultSource}
) =>
@ -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,

View File

@ -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,

View File

@ -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(),

View File

@ -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,

View File

@ -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 (
<div key={key} className={cellClass} style={style}>
{data[rowIndex][columnIndex]}
<div
key={key}
style={style}
className={cellClass}
onMouseOver={this.handleHover(columnIndex, rowIndex)}
>
{`${data[rowIndex][columnIndex]}`}
</div>
)
}
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 (
<div
className="table-graph-container"
ref={gridContainer => (this.gridContainer = gridContainer)}
onMouseOut={this.handleMouseOut}
>
{data.length > 1 &&
<MultiGrid
fixedColumnCount={1}
fixedRowCount={1}
cellRenderer={this.cellRenderer}
columnCount={columnCount}
columnWidth={COLUMN_WIDTH}
height={tableHeight}
rowCount={rowCount}
rowHeight={ROW_HEIGHT}
height={tableHeight}
width={tableWidth}
fixedColumnCount={1}
fixedRowCount={1}
enableFixedColumnScroll={true}
enableFixedRowScroll={true}
scrollToRow={hoverTimeRow}
cellRenderer={this.cellRenderer}
hoveredColumnIndex={hoveredColumnIndex}
hoveredRowIndex={hoveredRowIndex}
hoverTime={hoverTime}
/>}
</div>
)
}
}
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

View File

@ -0,0 +1,4 @@
export const NULL_COLUMN_INDEX = -1
export const NULL_ROW_INDEX = -1
export const NULL_HOVER_TIME = '0'

View File

@ -33,6 +33,7 @@
// Components
@import 'components/annotations';
@import 'components/crosshairs';
@import 'components/ceo-display-options';
@import 'components/confirm-button';
@import 'components/confirm-buttons';

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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