Merge pull request #4193 from influxdata/enhancements/refactor-tablegraph

Refactor table graph and multi grid to remove unused functionality
pull/4208/head
Brandon Farmer 2018-08-13 16:47:36 -07:00 committed by GitHub
commit ff2ce4f8d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 364 additions and 611 deletions

View File

@ -91,10 +91,10 @@ const updateMaxWidths = (
const currentWidth = useTimeWidth const currentWidth = useTimeWidth
? timeFormatWidth ? timeFormatWidth
: calculateSize(colValue, { : calculateSize(colValue.toString().trim(), {
font: isLabel ? '"Roboto"' : '"RobotoMono", monospace', font: isLabel ? '"Roboto"' : '"RobotoMono", monospace',
fontSize: '13px', fontSize: '12px',
fontWeight: 'bold', fontWeight: '500',
}).width + CELL_HORIZONTAL_PADDING }).width + CELL_HORIZONTAL_PADDING
const {widths: Widths} = maxColumnWidths const {widths: Widths} = maxColumnWidths
@ -151,6 +151,7 @@ export const calculateColumnWidths = (
const timeFormatWidth = calculateTimeColumnWidth( const timeFormatWidth = calculateTimeColumnWidth(
timeFormat === '' ? DEFAULT_TIME_FORMAT : timeFormat timeFormat === '' ? DEFAULT_TIME_FORMAT : timeFormat
) )
return fastReduce<TimeSeriesValue[], ColumnWidths>( return fastReduce<TimeSeriesValue[], ColumnWidths>(
data, data,
(acc: ColumnWidths, row: TimeSeriesValue[], r: number) => { (acc: ColumnWidths, row: TimeSeriesValue[], r: number) => {

View File

@ -74,6 +74,7 @@ class Layout extends Component<Props> {
) : ( ) : (
<RefreshingGraph <RefreshingGraph
onZoom={onZoom} onZoom={onZoom}
timeFormat={cell.timeFormat}
axes={cell.axes} axes={cell.axes}
type={cell.type} type={cell.type}
inView={cell.inView} inView={cell.inView}

View File

@ -1,9 +1,11 @@
import * as React from 'react' import * as React from 'react'
import CellMeasurerCacheDecorator from './CellMeasurerCacheDecorator' import _ from 'lodash'
import FancyScrollbar from 'src/shared/components/FancyScrollbar' import FancyScrollbar from 'src/shared/components/FancyScrollbar'
import {Grid} from 'react-virtualized' import {Grid, AutoSizer} from 'react-virtualized'
const SCROLLBAR_SIZE_BUFFER = 20 const SCROLLBAR_SIZE_BUFFER = 20
const ROW_HEIGHT = 30
type HeightWidthFunction = (arg: {index: number}) => number type HeightWidthFunction = (arg: {index: number}) => number
export interface PropsMultiGrid { export interface PropsMultiGrid {
@ -14,24 +16,16 @@ export interface PropsMultiGrid {
classNameBottomRightGrid?: string classNameBottomRightGrid?: string
classNameTopLeftGrid?: string classNameTopLeftGrid?: string
classNameTopRightGrid?: string classNameTopRightGrid?: string
enableFixedColumnScroll?: boolean
enableFixedRowScroll?: boolean
fixedColumnCount?: number fixedColumnCount?: number
fixedRowCount?: number fixedRowCount?: number
style?: object scrollToRow?: number
styleBottomLeftGrid?: object scrollToColumn?: number
styleBottomRightGrid?: object
styleTopLeftGrid?: object
styleTopRightGrid?: object
scrollTop?: number
scrollLeft?: number
rowCount?: number rowCount?: number
rowHeight?: number | HeightWidthFunction rowHeight?: number | HeightWidthFunction
columnWidth?: number | HeightWidthFunction columnWidth?: number | HeightWidthFunction
onScroll?: (arg: object) => {} onScroll?: (arg: object) => {}
onSectionRendered?: () => {}
cellRenderer?: (arg: object) => JSX.Element cellRenderer?: (arg: object) => JSX.Element
[key: string]: any // MultiGrid can accept any prop, and will rerender if they change onMount?: (mg: MultiGrid) => void
} }
interface State { interface State {
@ -42,83 +36,32 @@ interface State {
showVerticalScrollbar: boolean showVerticalScrollbar: boolean
} }
/**
* Renders 1, 2, or 4 Grids depending on configuration.
* A main (body) Grid will always be rendered.
* Optionally, 1-2 Grids for sticky header rows will also be rendered.
* If no sticky columns, only 1 sticky header Grid will be rendered.
* If sticky columns, 2 sticky header Grids will be rendered.
*/
class MultiGrid extends React.PureComponent<PropsMultiGrid, State> { class MultiGrid extends React.PureComponent<PropsMultiGrid, State> {
public static defaultProps = { public static defaultProps = {
classNameBottomLeftGrid: '', classNameBottomLeftGrid: '',
classNameBottomRightGrid: '', classNameBottomRightGrid: '',
classNameTopLeftGrid: '', classNameTopLeftGrid: '',
classNameTopRightGrid: '', classNameTopRightGrid: '',
enableFixedColumnScroll: false,
enableFixedRowScroll: false,
fixedColumnCount: 0, fixedColumnCount: 0,
fixedRowCount: 0, fixedRowCount: 0,
scrollToColumn: -1, scrollToColumn: -1,
scrollToRow: -1, scrollToRow: -1,
style: {},
styleBottomLeftGrid: {}, styleBottomLeftGrid: {},
styleBottomRightGrid: {}, styleBottomRightGrid: {},
styleTopLeftGrid: {}, styleTopLeftGrid: {},
styleTopRightGrid: {}, styleTopRightGrid: {},
} }
public static getDerivedStateFromProps(
nextProps: PropsMultiGrid,
prevState: State
) {
if (
nextProps.scrollLeft !== prevState.scrollLeft ||
nextProps.scrollTop !== prevState.scrollTop
) {
return {
scrollLeft:
nextProps.scrollLeft != null && nextProps.scrollLeft >= 0
? nextProps.scrollLeft
: prevState.scrollLeft,
scrollTop:
nextProps.scrollTop != null && nextProps.scrollTop >= 0
? nextProps.scrollTop
: prevState.scrollTop,
}
}
return null
}
private deferredInvalidateColumnIndex: number = 0
private deferredInvalidateRowIndex: number = 0
private bottomLeftGrid: Grid private bottomLeftGrid: Grid
private bottomRightGrid: Grid private bottomRightGrid: Grid
private topLeftGrid: Grid private topLeftGrid: Grid
private topRightGrid: Grid private topRightGrid: Grid
private deferredMeasurementCacheBottomLeftGrid: CellMeasurerCacheDecorator
private deferredMeasurementCacheBottomRightGrid: CellMeasurerCacheDecorator
private deferredMeasurementCacheTopRightGrid: CellMeasurerCacheDecorator
private leftGridWidth: number | null = 0 private leftGridWidth: number | null = 0
private topGridHeight: number | null = 0 private topGridHeight: number | null = 0
private lastRenderedColumnWidth: number | HeightWidthFunction
private lastRenderedFixedColumnCount: number = 0
private lastRenderedFixedRowCount: number = 0
private lastRenderedRowHeight: number | HeightWidthFunction
private bottomRightGridStyle: object | null
private topRightGridStyle: object | null private topRightGridStyle: object | null
private lastRenderedStyle: object | null
private lastRenderedHeight: number = 0
private lastRenderedWidth: number = 0
private containerTopStyle: object | null private containerTopStyle: object | null
private containerBottomStyle: object | null private containerBottomStyle: object | null
private containerOuterStyle: object | null private containerOuterStyle: object | null
private lastRenderedStyleBottomLeftGrid: object | null
private lastRenderedStyleBottomRightGrid: object | null
private lastRenderedStyleTopLeftGrid: object | null
private lastRenderedStyleTopRightGrid: object | null
private bottomLeftGridStyle: object | null
private topLeftGridStyle: object | null private topLeftGridStyle: object | null
constructor(props: PropsMultiGrid, context) { constructor(props: PropsMultiGrid, context) {
@ -131,82 +74,6 @@ class MultiGrid extends React.PureComponent<PropsMultiGrid, State> {
showHorizontalScrollbar: false, showHorizontalScrollbar: false,
showVerticalScrollbar: false, showVerticalScrollbar: false,
} }
const {deferredMeasurementCache, fixedColumnCount, fixedRowCount} = props
this.maybeCalculateCachedStyles(true)
if (deferredMeasurementCache) {
this.deferredMeasurementCacheBottomLeftGrid =
fixedRowCount > 0
? new CellMeasurerCacheDecorator({
cellMeasurerCache: deferredMeasurementCache,
columnIndexOffset: 0,
rowIndexOffset: fixedRowCount,
})
: deferredMeasurementCache
this.deferredMeasurementCacheBottomRightGrid =
fixedColumnCount > 0 || fixedRowCount > 0
? new CellMeasurerCacheDecorator({
cellMeasurerCache: deferredMeasurementCache,
columnIndexOffset: fixedColumnCount,
rowIndexOffset: fixedRowCount,
})
: deferredMeasurementCache
this.deferredMeasurementCacheTopRightGrid =
fixedColumnCount > 0
? new CellMeasurerCacheDecorator({
cellMeasurerCache: deferredMeasurementCache,
columnIndexOffset: fixedColumnCount,
rowIndexOffset: 0,
})
: deferredMeasurementCache
}
}
public forceUpdateGrids() {
if (this.bottomLeftGrid) {
this.bottomLeftGrid.forceUpdate()
}
if (this.bottomRightGrid) {
this.bottomRightGrid.forceUpdate()
}
if (this.topLeftGrid) {
this.topLeftGrid.forceUpdate()
}
if (this.topRightGrid) {
this.topRightGrid.forceUpdate()
}
}
/** See Grid#invalidateCellSizeAfterRender */
public invalidateCellSizeAfterRender({columnIndex = 0, rowIndex = 0} = {}) {
this.deferredInvalidateColumnIndex =
typeof this.deferredInvalidateColumnIndex === 'number'
? Math.min(this.deferredInvalidateColumnIndex, columnIndex)
: columnIndex
this.deferredInvalidateRowIndex =
typeof this.deferredInvalidateRowIndex === 'number'
? Math.min(this.deferredInvalidateRowIndex, rowIndex)
: rowIndex
}
/** See Grid#measureAllCells */
public measureAllCells() {
if (this.bottomLeftGrid) {
this.bottomLeftGrid.measureAllCells()
}
if (this.bottomRightGrid) {
this.bottomRightGrid.measureAllCells()
}
if (this.topLeftGrid) {
this.topLeftGrid.measureAllCells()
}
if (this.topRightGrid) {
this.topRightGrid.measureAllCells()
}
} }
public recomputeGridSize({columnIndex = 0, rowIndex = 0} = {}) { public recomputeGridSize({columnIndex = 0, rowIndex = 0} = {}) {
@ -248,50 +115,20 @@ class MultiGrid extends React.PureComponent<PropsMultiGrid, State> {
} }
public componentDidMount() { public componentDidMount() {
const {scrollLeft, scrollTop} = this.props if (this.props.onMount) {
this.props.onMount(this)
if (scrollLeft > 0 || scrollTop > 0) {
const newState: Partial<State> = {}
if (scrollLeft > 0) {
newState.scrollLeft = scrollLeft
} }
if (scrollTop > 0) {
newState.scrollTop = scrollTop
}
this.setState({...this.state, ...newState})
}
this.handleInvalidatedGridSize()
}
public componentDidUpdate() {
this.handleInvalidatedGridSize()
} }
public render() { public render() {
const { const {onScroll, ...rest} = this.props
onScroll,
scrollLeft: scrollLeftProp, // eslint-disable-line no-unused-vars
onSectionRendered,
scrollToRow,
scrollToColumn,
scrollTop: scrollTopProp, // eslint-disable-line no-unused-vars
...rest
} = this.props
this.prepareForRender() this.prepareForRender()
// Don't render any of our Grids if there are no cells.
// This mirrors what Grid does,
// And prevents us from recording inaccurage measurements when used with CellMeasurer.
if (this.props.width === 0 || this.props.height === 0) { if (this.props.width === 0 || this.props.height === 0) {
return null return null
} }
// scrollTop and scrollLeft props are explicitly filtered out and ignored
const {scrollLeft, scrollTop} = this.state const {scrollLeft, scrollTop} = this.state
return ( return (
@ -313,10 +150,7 @@ class MultiGrid extends React.PureComponent<PropsMultiGrid, State> {
{this.renderBottomRightGrid({ {this.renderBottomRightGrid({
...rest, ...rest,
onScroll, onScroll,
onSectionRendered,
scrollLeft, scrollLeft,
scrollToColumn,
scrollToRow,
scrollTop, scrollTop,
})} })}
</div> </div>
@ -340,20 +174,15 @@ class MultiGrid extends React.PureComponent<PropsMultiGrid, State> {
} else { } else {
return cellRenderer({ return cellRenderer({
...rest, ...rest,
style: {
...rest.style,
},
parent: this, parent: this,
rowIndex: rowIndex + fixedRowCount, rowIndex: rowIndex + fixedRowCount,
}) })
} }
} }
private getBottomGridHeight(props: PropsMultiGrid) {
const {height} = props
const topGridHeight = this.getTopGridHeight(props)
return height - topGridHeight
}
private getLeftGridWidth(props: PropsMultiGrid) { private getLeftGridWidth(props: PropsMultiGrid) {
const {fixedColumnCount, columnWidth} = props const {fixedColumnCount, columnWidth} = props
@ -376,11 +205,7 @@ class MultiGrid extends React.PureComponent<PropsMultiGrid, State> {
private getRightGridWidth(props: PropsMultiGrid) { private getRightGridWidth(props: PropsMultiGrid) {
const {width} = props const {width} = props
return width - this.getLeftGridWidth(props)
const leftGridWidth = this.getLeftGridWidth(props)
const result = width - leftGridWidth
return result
} }
private getTopGridHeight(props: PropsMultiGrid) { private getTopGridHeight(props: PropsMultiGrid) {
@ -404,59 +229,72 @@ class MultiGrid extends React.PureComponent<PropsMultiGrid, State> {
} }
private onScrollbarsScroll = (e: React.MouseEvent<JSX.Element>) => { private onScrollbarsScroll = (e: React.MouseEvent<JSX.Element>) => {
const {target} = e const {scrollTop} = e.target as HTMLElement
this.onScroll(target) const {scrollLeft} = this.state
this.onScroll({scrollTop, scrollLeft})
}
private onGridScroll = ({scrollLeft}) => {
const {scrollTop} = this.state
this.onScroll({scrollTop, scrollLeft})
} }
private onScroll = scrollInfo => { private onScroll = scrollInfo => {
const {scrollLeft, scrollTop} = scrollInfo
this.setState({
scrollLeft,
scrollTop,
})
const {onScroll} = this.props const {onScroll} = this.props
const {scrollLeft, scrollTop} = scrollInfo
this.setState({scrollLeft, scrollTop})
if (onScroll) { if (onScroll) {
onScroll(scrollInfo) onScroll(scrollInfo)
} }
} }
private onScrollLeft = scrollInfo => {
const {scrollLeft} = scrollInfo
this.onScroll({
scrollLeft,
scrollTop: this.state.scrollTop,
})
}
private renderBottomLeftGrid(props) { private renderBottomLeftGrid(props) {
const {fixedColumnCount, fixedRowCount, rowCount} = props const {fixedColumnCount, fixedRowCount, rowCount, columnWidth} = props
if (!fixedColumnCount) { if (!fixedColumnCount) {
return null return null
} }
const width = this.getLeftGridWidth(props) const calculatedRowCount = Math.max(rowCount - fixedRowCount, 0)
const height = this.getBottomGridHeight(props)
return ( return (
<AutoSizer>
{({width, height}) => (
<FancyScrollbar
style={{
width,
height: this.props.height - ROW_HEIGHT,
}}
autoHide={true}
scrollTop={this.state.scrollTop}
scrollLeft={this.state.scrollLeft}
setScrollTop={this.onScrollbarsScroll}
>
<Grid <Grid
{...props} {...props}
cellRenderer={this.cellRendererBottomLeftGrid} cellRenderer={this.cellRendererBottomLeftGrid}
className={this.props.classNameBottomLeftGrid} className={this.props.classNameBottomLeftGrid}
columnCount={fixedColumnCount} columnCount={fixedColumnCount}
deferredMeasurementCache={this.deferredMeasurementCacheBottomLeftGrid}
onScroll={this.onScroll}
height={height} height={height}
ref={this.bottomLeftGridRef} ref={this.bottomLeftGridRef}
rowCount={Math.max(0, rowCount - fixedRowCount)} rowCount={calculatedRowCount}
rowHeight={this.rowHeightBottomGrid} rowHeight={ROW_HEIGHT}
columnWidth={columnWidth}
style={{ style={{
...this.bottomLeftGridStyle, overflowY: 'hidden',
height: calculatedRowCount * ROW_HEIGHT,
position: 'absolute',
}} }}
tabIndex={null} tabIndex={null}
width={width} width={width}
/> />
</FancyScrollbar>
)}
</AutoSizer>
) )
} }
@ -466,46 +304,51 @@ class MultiGrid extends React.PureComponent<PropsMultiGrid, State> {
fixedColumnCount, fixedColumnCount,
fixedRowCount, fixedRowCount,
rowCount, rowCount,
scrollToColumn,
scrollToRow, scrollToRow,
scrollLeft,
scrollTop,
} = props } = props
const width = this.getRightGridWidth(props) const calculatedRowCount = Math.max(0, rowCount - fixedRowCount)
const height = this.getBottomGridHeight(props)
const leftWidth = this.getLeftGridWidth(props)
return ( return (
<AutoSizer>
{({width, height}) => (
<FancyScrollbar <FancyScrollbar
style={{...this.bottomRightGridStyle, width, height}} style={{
marginLeft: leftWidth,
width: this.props.width - leftWidth,
height: this.props.height - ROW_HEIGHT,
}}
autoHide={true} autoHide={true}
scrollTop={this.state.scrollTop} scrollTop={scrollTop}
scrollLeft={this.state.scrollLeft} scrollLeft={scrollLeft}
setScrollTop={this.onScrollbarsScroll} setScrollTop={this.onScrollbarsScroll}
> >
<Grid <Grid
{...props} {..._.omit(props, ['scrollToColumn'])}
cellRenderer={this.cellRendererBottomRightGrid} cellRenderer={this.cellRendererBottomRightGrid}
className={this.props.classNameBottomRightGrid} className={this.props.classNameBottomRightGrid}
columnCount={Math.max(0, columnCount - fixedColumnCount)} columnCount={Math.max(0, columnCount - fixedColumnCount)}
columnWidth={this.columnWidthRightGrid} columnWidth={this.columnWidthRightGrid}
deferredMeasurementCache={ overscanRowCount={100}
this.deferredMeasurementCacheBottomRightGrid
}
height={height} height={height}
ref={this.bottomRightGridRef} ref={this.bottomRightGridRef}
rowCount={Math.max(0, rowCount - fixedRowCount)} onScroll={this.onGridScroll}
rowHeight={this.rowHeightBottomGrid} rowCount={calculatedRowCount}
onScroll={this.onScroll} rowHeight={ROW_HEIGHT}
scrollToColumn={scrollToColumn - fixedColumnCount}
scrollToRow={scrollToRow - fixedRowCount} scrollToRow={scrollToRow - fixedRowCount}
style={{ style={{
...this.bottomRightGridStyle, overflowY: 'hidden',
overflowX: false, height: calculatedRowCount * ROW_HEIGHT + SCROLLBAR_SIZE_BUFFER,
overflowY: true,
left: 0,
}} }}
width={width} width={width - leftWidth}
/> />
</FancyScrollbar> </FancyScrollbar>
)}
</AutoSizer>
) )
} }
@ -532,13 +375,7 @@ class MultiGrid extends React.PureComponent<PropsMultiGrid, State> {
} }
private renderTopRightGrid(props) { private renderTopRightGrid(props) {
const { const {columnCount, fixedColumnCount, fixedRowCount, scrollLeft} = props
columnCount,
enableFixedRowScroll,
fixedColumnCount,
fixedRowCount,
scrollLeft,
} = props
if (!fixedRowCount) { if (!fixedRowCount) {
return null return null
@ -554,9 +391,8 @@ class MultiGrid extends React.PureComponent<PropsMultiGrid, State> {
className={this.props.classNameTopRightGrid} className={this.props.classNameTopRightGrid}
columnCount={Math.max(0, columnCount - fixedColumnCount)} columnCount={Math.max(0, columnCount - fixedColumnCount)}
columnWidth={this.columnWidthRightGrid} columnWidth={this.columnWidthRightGrid}
deferredMeasurementCache={this.deferredMeasurementCacheTopRightGrid}
height={height} height={height}
onScroll={enableFixedRowScroll ? this.onScrollLeft : undefined} onScroll={this.onGridScroll}
ref={this.topRightGridRef} ref={this.topRightGridRef}
rowCount={fixedRowCount} rowCount={fixedRowCount}
scrollLeft={scrollLeft} scrollLeft={scrollLeft}
@ -567,23 +403,6 @@ class MultiGrid extends React.PureComponent<PropsMultiGrid, State> {
) )
} }
private rowHeightBottomGrid = ({index}) => {
const {fixedRowCount, rowCount, rowHeight} = this.props
const {scrollbarSize, showVerticalScrollbar} = this.state
// An extra cell is added to the count
// This gives the smaller Grid extra room for offset,
// In case the main (bottom right) Grid has a scrollbar
// If no scrollbar, the extra space is overflow:hidden anyway
if (showVerticalScrollbar && index === rowCount - fixedRowCount) {
return scrollbarSize
}
return typeof rowHeight === 'function'
? rowHeight({index: index + fixedRowCount})
: rowHeight
}
private topLeftGridRef = ref => { private topLeftGridRef = ref => {
this.topLeftGrid = ref this.topLeftGrid = ref
} }
@ -597,43 +416,17 @@ class MultiGrid extends React.PureComponent<PropsMultiGrid, State> {
* This method recalculates styles only when specific props change. * This method recalculates styles only when specific props change.
*/ */
private maybeCalculateCachedStyles(resetAll) { private maybeCalculateCachedStyles(resetAll) {
const { const {height, width} = this.props
columnWidth,
height,
fixedColumnCount,
fixedRowCount,
rowHeight,
style,
styleBottomLeftGrid,
styleBottomRightGrid,
styleTopLeftGrid,
styleTopRightGrid,
width,
} = this.props
const sizeChange = if (resetAll) {
resetAll ||
height !== this.lastRenderedHeight ||
width !== this.lastRenderedWidth
const leftSizeChange =
resetAll ||
columnWidth !== this.lastRenderedColumnWidth ||
fixedColumnCount !== this.lastRenderedFixedColumnCount
const topSizeChange =
resetAll ||
fixedRowCount !== this.lastRenderedFixedRowCount ||
rowHeight !== this.lastRenderedRowHeight
if (resetAll || sizeChange || style !== this.lastRenderedStyle) {
this.containerOuterStyle = { this.containerOuterStyle = {
height, height,
overflow: 'visible', // Let :focus outline show through overflow: 'visible', // Let :focus outline show through
width, width,
...style,
} }
} }
if (resetAll || sizeChange || topSizeChange) { if (resetAll) {
this.containerTopStyle = { this.containerTopStyle = {
height: this.getTopGridHeight(this.props), height: this.getTopGridHeight(this.props),
position: 'relative', position: 'relative',
@ -648,68 +441,25 @@ class MultiGrid extends React.PureComponent<PropsMultiGrid, State> {
} }
} }
if ( if (resetAll) {
resetAll ||
styleBottomLeftGrid !== this.lastRenderedStyleBottomLeftGrid
) {
this.bottomLeftGridStyle = {
left: 0,
overflowY: 'hidden',
overflowX: 'hidden',
position: 'absolute',
...styleBottomLeftGrid,
}
}
if (
resetAll ||
leftSizeChange ||
styleBottomRightGrid !== this.lastRenderedStyleBottomRightGrid
) {
this.bottomRightGridStyle = {
left: this.getLeftGridWidth(this.props),
position: 'absolute',
...styleBottomRightGrid,
}
}
if (resetAll || styleTopLeftGrid !== this.lastRenderedStyleTopLeftGrid) {
this.topLeftGridStyle = { this.topLeftGridStyle = {
left: 0, left: 0,
overflowX: 'hidden', overflowX: 'hidden',
overflowY: 'hidden', overflowY: 'hidden',
position: 'absolute', position: 'absolute',
top: 0, top: 0,
...styleTopLeftGrid,
} }
} }
if ( if (resetAll) {
resetAll ||
leftSizeChange ||
styleTopRightGrid !== this.lastRenderedStyleTopRightGrid
) {
this.topRightGridStyle = { this.topRightGridStyle = {
left: this.getLeftGridWidth(this.props), left: this.getLeftGridWidth(this.props),
overflowX: 'hidden', overflowX: 'hidden',
overflowY: 'hidden', overflowY: 'hidden',
position: 'absolute', position: 'absolute',
top: 0, top: 0,
...styleTopRightGrid,
} }
} }
this.lastRenderedColumnWidth = columnWidth
this.lastRenderedFixedColumnCount = fixedColumnCount
this.lastRenderedFixedRowCount = fixedRowCount
this.lastRenderedHeight = height
this.lastRenderedRowHeight = rowHeight
this.lastRenderedStyle = style
this.lastRenderedStyleBottomLeftGrid = styleBottomLeftGrid
this.lastRenderedStyleBottomRightGrid = styleBottomRightGrid
this.lastRenderedStyleTopLeftGrid = styleTopLeftGrid
this.lastRenderedStyleTopRightGrid = styleTopRightGrid
this.lastRenderedWidth = width
} }
private bottomLeftGridRef = ref => { private bottomLeftGridRef = ref => {
@ -765,48 +515,13 @@ class MultiGrid extends React.PureComponent<PropsMultiGrid, State> {
return scrollbarSize return scrollbarSize
} }
return typeof columnWidth === 'function' return _.isFunction(columnWidth)
? columnWidth({index: index + fixedColumnCount}) ? columnWidth({index: index + fixedColumnCount})
: columnWidth : columnWidth
} }
private handleInvalidatedGridSize() {
if (typeof this.deferredInvalidateColumnIndex === 'number') {
const columnIndex = this.deferredInvalidateColumnIndex
const rowIndex = this.deferredInvalidateRowIndex
this.deferredInvalidateColumnIndex = null
this.deferredInvalidateRowIndex = null
this.recomputeGridSize({
columnIndex,
rowIndex,
})
this.forceUpdate()
}
}
private prepareForRender() { private prepareForRender() {
if (
this.lastRenderedColumnWidth !== this.props.columnWidth ||
this.lastRenderedFixedColumnCount !== this.props.fixedColumnCount
) {
this.leftGridWidth = null
}
if (
this.lastRenderedFixedRowCount !== this.props.fixedRowCount ||
this.lastRenderedRowHeight !== this.props.rowHeight
) {
this.topGridHeight = null
}
this.maybeCalculateCachedStyles(false) this.maybeCalculateCachedStyles(false)
this.lastRenderedColumnWidth = this.props.columnWidth
this.lastRenderedFixedColumnCount = this.props.fixedColumnCount
this.lastRenderedFixedRowCount = this.props.fixedRowCount
this.lastRenderedRowHeight = this.props.rowHeight
} }
} }

View File

@ -2,14 +2,12 @@ import React, {Component} from 'react'
import _ from 'lodash' import _ from 'lodash'
import classnames from 'classnames' import classnames from 'classnames'
import {connect} from 'react-redux' import {connect} from 'react-redux'
import {ColumnSizer, SizedColumnProps} from 'react-virtualized'
import {MultiGrid, PropsMultiGrid} from 'src/shared/components/MultiGrid'
import {bindActionCreators} from 'redux'
import moment from 'moment' import moment from 'moment'
import {ColumnSizer, SizedColumnProps, AutoSizer} from 'react-virtualized'
import {MultiGrid, PropsMultiGrid} from 'src/shared/components/MultiGrid'
import {bindActionCreators} from 'redux'
import {fastReduce} from 'src/utils/fast' import {fastReduce} from 'src/utils/fast'
import {timeSeriesToTableGraph} from 'src/utils/timeSeriesTransformers' import {timeSeriesToTableGraph} from 'src/utils/timeSeriesTransformers'
import { import {
computeFieldOptions, computeFieldOptions,
@ -28,7 +26,6 @@ import {
} from 'src/shared/constants/tableGraph' } from 'src/shared/constants/tableGraph'
import {generateThresholdsListHexs} from 'src/shared/constants/colorOperations' import {generateThresholdsListHexs} from 'src/shared/constants/colorOperations'
import {ErrorHandling} from 'src/shared/decorators/errors' import {ErrorHandling} from 'src/shared/decorators/errors'
import {TimeSeriesServerResponse, TimeSeriesValue} from 'src/types/series' import {TimeSeriesServerResponse, TimeSeriesValue} from 'src/types/series'
import {ColorString} from 'src/types/colors' import {ColorString} from 'src/types/colors'
import { import {
@ -38,6 +35,9 @@ import {
Sort, Sort,
} from 'src/types/dashboards' } from 'src/types/dashboards'
const COLUMN_MIN_WIDTH = 100
const ROW_HEIGHT = 30
interface Label { interface Label {
label: string label: string
seriesIndex: number seriesIndex: number
@ -49,7 +49,7 @@ interface CellRendererProps {
rowIndex: number rowIndex: number
key: string key: string
parent: React.Component<PropsMultiGrid> parent: React.Component<PropsMultiGrid>
style: {[x: string]: any} style: React.CSSProperties
} }
interface Props { interface Props {
@ -77,11 +77,14 @@ interface State {
columnWidths: {[x: string]: number} columnWidths: {[x: string]: number}
totalColumnWidths: number totalColumnWidths: number
isTimeVisible: boolean isTimeVisible: boolean
shouldResize: boolean
} }
@ErrorHandling @ErrorHandling
class TableGraph extends Component<Props, State> { class TableGraph extends Component<Props, State> {
private gridContainer: HTMLDivElement private gridContainer: HTMLDivElement
private multiGrid?: MultiGrid
constructor(props: Props) { constructor(props: Props) {
super(props) super(props)
@ -91,6 +94,7 @@ class TableGraph extends Component<Props, State> {
DEFAULT_TIME_FIELD.internalName DEFAULT_TIME_FIELD.internalName
) )
this.state = { this.state = {
shouldResize: false,
data: [[]], data: [[]],
transformedData: [[]], transformedData: [[]],
sortedTimeVals: [], sortedTimeVals: [],
@ -106,34 +110,13 @@ class TableGraph extends Component<Props, State> {
} }
public render() { public render() {
const { const {transformedData} = this.state
hoveredColumnIndex,
hoveredRowIndex,
timeColumnWidth,
sort,
transformedData,
} = this.state
const {
hoverTime,
tableOptions,
colors,
fieldOptions,
timeFormat,
decimalPlaces,
} = this.props
const {fixFirstColumn = DEFAULT_FIX_FIRST_COLUMN} = tableOptions
const columnCount = _.get(transformedData, ['0', 'length'], 0) const columnCount = _.get(transformedData, ['0', 'length'], 0)
const rowCount = columnCount === 0 ? 0 : transformedData.length const rowCount = columnCount === 0 ? 0 : transformedData.length
const COLUMN_MIN_WIDTH = 100 const fixedColumnCount = this.fixFirstColumn && columnCount > 1 ? 1 : 0
const COLUMN_MAX_WIDTH = 1000
const ROW_HEIGHT = 30
const fixedColumnCount = fixFirstColumn && columnCount > 1 ? 1 : undefined
const tableWidth = _.get(this, ['gridContainer', 'clientWidth'], 0)
const tableHeight = _.get(this, ['gridContainer', 'clientHeight'], 0)
const {scrollToColumn, scrollToRow} = this.scrollToColRow const {scrollToColumn, scrollToRow} = this.scrollToColRow
return ( return (
<div <div
className="table-graph-container" className="table-graph-container"
@ -141,48 +124,64 @@ class TableGraph extends Component<Props, State> {
onMouseLeave={this.handleMouseLeave} onMouseLeave={this.handleMouseLeave}
> >
{rowCount > 0 && ( {rowCount > 0 && (
<AutoSizer>
{({width, height}) => (
<ColumnSizer <ColumnSizer
columnCount={columnCount} columnCount={this.computedColumnCount}
columnMaxWidth={COLUMN_MAX_WIDTH}
columnMinWidth={COLUMN_MIN_WIDTH} columnMinWidth={COLUMN_MIN_WIDTH}
width={tableWidth} width={width}
> >
{({columnWidth, registerChild}: SizedColumnProps) => ( {({
adjustedWidth,
columnWidth,
registerChild,
}: SizedColumnProps) => (
<MultiGrid <MultiGrid
onMount={this.handleMultiGridMount}
ref={registerChild} ref={registerChild}
columnCount={columnCount} columnCount={columnCount}
columnWidth={this.calculateColumnWidth(columnWidth)} columnWidth={this.calculateColumnWidth(columnWidth)}
rowCount={rowCount}
rowHeight={ROW_HEIGHT}
height={tableHeight}
width={tableWidth}
fixedColumnCount={fixedColumnCount}
fixedRowCount={1}
enableFixedColumnScroll={true}
enableFixedRowScroll={true}
scrollToRow={scrollToRow} scrollToRow={scrollToRow}
scrollToColumn={scrollToColumn} scrollToColumn={scrollToColumn}
sort={sort} rowCount={rowCount}
rowHeight={ROW_HEIGHT}
height={height}
width={adjustedWidth}
fixedColumnCount={fixedColumnCount}
fixedRowCount={1}
cellRenderer={this.cellRenderer} cellRenderer={this.cellRenderer}
hoveredColumnIndex={hoveredColumnIndex}
hoveredRowIndex={hoveredRowIndex}
hoverTime={hoverTime}
colors={colors}
fieldOptions={fieldOptions}
tableOptions={tableOptions}
timeFormat={timeFormat}
decimalPlaces={decimalPlaces}
timeColumnWidth={timeColumnWidth}
classNameBottomRightGrid="table-graph--scroll-window" classNameBottomRightGrid="table-graph--scroll-window"
/> />
)} )}
</ColumnSizer> </ColumnSizer>
)} )}
</AutoSizer>
)}
</div> </div>
) )
} }
public handleMultiGridMount = (ref: MultiGrid) => {
this.multiGrid = ref
ref.forceUpdate()
}
public componentWillUnmount() {
window.removeEventListener('resize', this.handleResize)
}
public get timeField() {
const {fieldOptions} = this.props
return _.find(
fieldOptions,
f => f.internalName === DEFAULT_TIME_FIELD.internalName
)
}
public componentDidMount() { public componentDidMount() {
window.addEventListener('resize', this.handleResize)
const sortField: string = _.get( const sortField: string = _.get(
this.props, this.props,
['tableOptions', 'sortBy', 'internalName'], ['tableOptions', 'sortBy', 'internalName'],
@ -199,12 +198,10 @@ class TableGraph extends Component<Props, State> {
} = this.props } = this.props
const result = timeSeriesToTableGraph(data) const result = timeSeriesToTableGraph(data)
const sortedLabels = result.sortedLabels const sortedLabels = result.sortedLabels
const computedFieldOptions = computeFieldOptions(fieldOptions, sortedLabels) const computedFieldOptions = computeFieldOptions(fieldOptions, sortedLabels)
if (!_.isEqual(computedFieldOptions, fieldOptions)) {
this.handleUpdateFieldOptions(computedFieldOptions) this.handleUpdateFieldOptions(computedFieldOptions)
}
const {transformedData, sortedTimeVals, columnWidths} = transformTableData( const {transformedData, sortedTimeVals, columnWidths} = transformTableData(
result.data, result.data,
sort, sort,
@ -214,13 +211,10 @@ class TableGraph extends Component<Props, State> {
decimalPlaces decimalPlaces
) )
const timeField = _.find( const isTimeVisible = _.get(this.timeField, 'visible', true)
fieldOptions,
f => f.internalName === DEFAULT_TIME_FIELD.internalName
)
const isTimeVisible = _.get(timeField, 'visible', this.state.isTimeVisible)
this.setState({ this.setState(
{
transformedData, transformedData,
sortedTimeVals, sortedTimeVals,
columnWidths: columnWidths.widths, columnWidths: columnWidths.widths,
@ -231,50 +225,51 @@ class TableGraph extends Component<Props, State> {
hoveredRowIndex: NULL_ARRAY_INDEX, hoveredRowIndex: NULL_ARRAY_INDEX,
sort, sort,
isTimeVisible, isTimeVisible,
}) },
() => {
window.setTimeout(() => {
this.forceUpdate()
}, 0)
}
)
} }
public componentWillReceiveProps(nextProps: Props) { public componentWillReceiveProps(nextProps: Props) {
const updatedProps = _.keys(nextProps).filter( const {sort} = this.state
k => !_.isEqual(this.props[k], nextProps[k])
)
const {tableOptions, fieldOptions, timeFormat, decimalPlaces} = nextProps
let result = {} let result = {}
if (this.hasDataChanged(nextProps.data)) {
if (_.includes(updatedProps, 'data')) {
result = timeSeriesToTableGraph(nextProps.data) result = timeSeriesToTableGraph(nextProps.data)
} }
const data = _.get(result, 'data', this.state.data) const data = _.get(result, 'data', this.state.data)
const sortedLabels = _.get(result, 'sortedLabels', this.state.sortedLabels)
const computedFieldOptions = computeFieldOptions(fieldOptions, sortedLabels)
if (_.includes(updatedProps, 'data')) {
this.handleUpdateFieldOptions(computedFieldOptions)
}
if (_.isEmpty(data[0])) { if (_.isEmpty(data[0])) {
return return
} }
const {sort} = this.state const updatedProps = _.keys(_.omit(nextProps, 'data')).filter(
const internalName = _.get( k => !_.isEqual(this.props[k], nextProps[k])
nextProps,
['tableOptions', 'sortBy', 'internalName'],
''
) )
const {tableOptions, fieldOptions, timeFormat, decimalPlaces} = nextProps
const sortedLabels = _.get(result, 'sortedLabels', this.state.sortedLabels)
const computedFieldOptions = computeFieldOptions(fieldOptions, sortedLabels)
if (this.hasDataChanged(nextProps.data)) {
this.handleUpdateFieldOptions(computedFieldOptions)
}
const internalName = _.get(tableOptions, 'sortBy.internalName', '')
if ( if (
!_.get(this.props, ['tableOptions', 'sortBy', 'internalName'], '') === _.get(this.props, 'tableOptions.sortBy.internalName', '') !== internalName
internalName
) { ) {
sort.direction = DEFAULT_SORT_DIRECTION sort.direction = DEFAULT_SORT_DIRECTION
sort.field = internalName sort.field = internalName
} }
if ( if (
_.includes(updatedProps, 'data') || this.hasDataChanged(nextProps.data) ||
_.includes(updatedProps, 'tableOptions') || _.includes(updatedProps, 'tableOptions') ||
_.includes(updatedProps, 'fieldOptions') || _.includes(updatedProps, 'fieldOptions') ||
_.includes(updatedProps, 'timeFormat') _.includes(updatedProps, 'timeFormat')
@ -310,33 +305,75 @@ class TableGraph extends Component<Props, State> {
columnWidths: columnWidths.widths, columnWidths: columnWidths.widths,
totalColumnWidths: columnWidths.totalWidths, totalColumnWidths: columnWidths.totalWidths,
isTimeVisible, isTimeVisible,
shouldResize: true,
}) })
} }
} }
private handleUpdateFieldOptions = (fieldOptions: FieldOption[]): void => { public componentDidUpdate() {
const {isInCEO} = this.props if (this.state.shouldResize) {
if (!isInCEO) { if (this.multiGrid) {
return this.multiGrid.recomputeGridSize()
} }
this.props.handleUpdateFieldOptions(fieldOptions)
this.setState({shouldResize: false})
}
}
private hasDataChanged(data): boolean {
const newUUID = _.get(data, '0.response.uuid', null)
const oldUUID = _.get(this.props.data, '0.response.uuid', null)
return newUUID !== oldUUID
}
private get fixFirstColumn(): boolean {
const {tableOptions} = this.props
const {fixFirstColumn = DEFAULT_FIX_FIRST_COLUMN} = tableOptions
return fixFirstColumn
}
private get columnCount(): number {
const {transformedData} = this.state
return _.get(transformedData, ['0', 'length'], 0)
}
private get computedColumnCount(): number {
if (this.fixFirstColumn) {
return this.columnCount - 1
}
return this.columnCount
}
private get tableWidth(): number {
const tableWidth = _.get(this, ['gridContainer', 'clientWidth'], 0)
return tableWidth
}
private handleUpdateFieldOptions = (fieldOptions: FieldOption[]): void => {
const {isInCEO, handleUpdateFieldOptions} = this.props
if (isInCEO) {
handleUpdateFieldOptions(fieldOptions)
}
}
private get isEmpty(): boolean {
const {data} = this.state
return _.isEmpty(data[0])
} }
private get scrollToColRow(): { private get scrollToColRow(): {
scrollToRow: number | null scrollToRow: number | null
scrollToColumn: number | null scrollToColumn: number | null
} { } {
const {data, sortedTimeVals, hoveredColumnIndex, isTimeVisible} = this.state const {sortedTimeVals, hoveredColumnIndex, isTimeVisible} = this.state
const {hoverTime, tableOptions} = this.props const {hoverTime} = this.props
const hoveringThisTable = hoveredColumnIndex !== NULL_ARRAY_INDEX const hoveringThisTable = hoveredColumnIndex !== NULL_ARRAY_INDEX
const notHovering = hoverTime === NULL_HOVER_TIME const notHovering = hoverTime === NULL_HOVER_TIME
if ( if (this.isEmpty || notHovering || hoveringThisTable || !isTimeVisible) {
_.isEmpty(data[0]) || return {scrollToColumn: 0, scrollToRow: -1}
notHovering ||
hoveringThisTable ||
!isTimeVisible
) {
return {scrollToColumn: null, scrollToRow: null}
} }
const firstDiff = Math.abs(Number(hoverTime) - Number(sortedTimeVals[1])) // sortedTimeVals[0] is "time" const firstDiff = Math.abs(Number(hoverTime) - Number(sortedTimeVals[1])) // sortedTimeVals[0] is "time"
@ -355,33 +392,35 @@ class TableGraph extends Component<Props, State> {
{index: 1, diff: firstDiff} {index: 1, diff: firstDiff}
) )
const {verticalTimeAxis} = tableOptions const scrollToColumn = this.isVerticalTimeAxis ? -1 : hoverTimeFound.index
const scrollToColumn = verticalTimeAxis ? null : hoverTimeFound.index const scrollToRow = this.isVerticalTimeAxis ? hoverTimeFound.index : null
const scrollToRow = verticalTimeAxis ? hoverTimeFound.index : null
return {scrollToRow, scrollToColumn} return {scrollToRow, scrollToColumn}
} }
private handleHover = ( private get isVerticalTimeAxis(): boolean {
columnIndex: number, return _.get(
rowIndex: number this.props,
): (() => void) => (): void => { 'tableOptions.verticalTimeAxis',
const { DEFAULT_VERTICAL_TIME_AXIS
handleSetHoverTime, )
tableOptions: {verticalTimeAxis}, }
} = this.props
private handleHover = (e: React.MouseEvent<HTMLElement>) => {
const {dataset} = e.target as HTMLElement
const {handleSetHoverTime} = this.props
const {sortedTimeVals, isTimeVisible} = this.state const {sortedTimeVals, isTimeVisible} = this.state
if (verticalTimeAxis && rowIndex === 0) { if (this.isVerticalTimeAxis && +dataset.rowIndex === 0) {
return return
} }
if (handleSetHoverTime && isTimeVisible) { if (handleSetHoverTime && isTimeVisible) {
const hoverTime = verticalTimeAxis const hoverTime = this.isVerticalTimeAxis
? sortedTimeVals[rowIndex] ? sortedTimeVals[dataset.rowIndex]
: sortedTimeVals[columnIndex] : sortedTimeVals[dataset.columnIndex]
handleSetHoverTime(hoverTime.toString()) handleSetHoverTime(_.defaultTo(hoverTime, '').toString())
} }
this.setState({ this.setState({
hoveredColumnIndex: columnIndex, hoveredColumnIndex: +dataset.columnIndex,
hoveredRowIndex: rowIndex, hoveredRowIndex: +dataset.rowIndex,
}) })
} }
@ -426,31 +465,28 @@ class TableGraph extends Component<Props, State> {
index: number index: number
}): number => { }): number => {
const {index} = column const {index} = column
const {
tableOptions: {fixFirstColumn},
} = this.props
const {transformedData, columnWidths, totalColumnWidths} = this.state const {transformedData, columnWidths, totalColumnWidths} = this.state
const columnCount = _.get(transformedData, ['0', 'length'], 0)
const columnLabel = transformedData[0][index] const columnLabel = transformedData[0][index]
let adjustedColumnSizerWidth = columnWidths[columnLabel] const original = columnWidths[columnLabel]
const tableWidth = _.get(this, ['gridContainer', 'clientWidth'], 0) if (this.fixFirstColumn && index === 0) {
if (tableWidth > totalColumnWidths) { return original
if (columnCount === 1) { }
if (this.tableWidth <= totalColumnWidths) {
return original
}
if (this.columnCount <= 1) {
return columnSizerWidth return columnSizerWidth
} }
const difference = tableWidth - totalColumnWidths
const distributeOver =
fixFirstColumn && columnCount > 1 ? columnCount - 1 : columnCount
const increment = difference / distributeOver
adjustedColumnSizerWidth =
fixFirstColumn && index === 0
? columnWidths[columnLabel]
: columnWidths[columnLabel] + increment
}
return adjustedColumnSizerWidth const difference = this.tableWidth - totalColumnWidths
const increment = difference / this.computedColumnCount
return original + increment
} }
private createCellContents = ( private createCellContents = (
@ -460,16 +496,34 @@ class TableGraph extends Component<Props, State> {
isFieldName: boolean isFieldName: boolean
): string => { ): string => {
const {timeFormat, decimalPlaces} = this.props const {timeFormat, decimalPlaces} = this.props
if (isTimeData) { if (isTimeData) {
return `${moment(cellData).format(timeFormat)}` return moment(cellData).format(timeFormat)
} }
if (typeof cellData === 'string' && isFieldName) { if (_.isString(cellData) && isFieldName) {
return `${fieldName}` return _.defaultTo(fieldName, '').toString()
} }
if (typeof cellData === 'number' && decimalPlaces.isEnforced) { if (
_.isNumber(cellData) &&
decimalPlaces.isEnforced &&
decimalPlaces.digits < 100
) {
return cellData.toFixed(decimalPlaces.digits) return cellData.toFixed(decimalPlaces.digits)
} }
return `${cellData}`
return _.defaultTo(cellData, '').toString()
}
private handleResize = () => {
this.forceUpdate()
}
private get timeFieldIndex(): number {
const {fieldOptions = [DEFAULT_TIME_FIELD]} = this.props
return fieldOptions.findIndex(
({internalName}) => internalName === DEFAULT_TIME_FIELD.internalName
)
} }
private cellRenderer = ({ private cellRenderer = ({
@ -487,43 +541,24 @@ class TableGraph extends Component<Props, State> {
isTimeVisible, isTimeVisible,
} = this.state } = this.state
const { const {fieldOptions = [DEFAULT_TIME_FIELD], colors} = this.props
fieldOptions = [DEFAULT_TIME_FIELD],
tableOptions,
colors,
} = this.props
const {
verticalTimeAxis = DEFAULT_VERTICAL_TIME_AXIS,
fixFirstColumn = DEFAULT_FIX_FIRST_COLUMN,
} = tableOptions
const cellData = transformedData[rowIndex][columnIndex] const cellData = transformedData[rowIndex][columnIndex]
const isSorted = sort.field === cellData
const timeFieldIndex = fieldOptions.findIndex( const isAscending = sort.direction === ASCENDING
field => field.internalName === DEFAULT_TIME_FIELD.internalName const isFirstRow = rowIndex === 0
) const isFirstCol = columnIndex === 0
const isFixedRow = isFirstRow && !isFirstCol
const isFixedRow = rowIndex === 0 && columnIndex > 0 const isFixedColumn = this.fixFirstColumn && !isFirstRow && isFirstCol
const isFixedColumn = fixFirstColumn && rowIndex > 0 && columnIndex === 0
const isTimeData = const isTimeData =
isTimeVisible && isTimeVisible &&
(verticalTimeAxis (this.isVerticalTimeAxis
? rowIndex !== 0 && columnIndex === timeFieldIndex ? !isFirstRow && columnIndex === this.timeFieldIndex
: rowIndex === timeFieldIndex && columnIndex !== 0) : rowIndex === this.timeFieldIndex && isFirstCol)
const isFieldName = verticalTimeAxis ? rowIndex === 0 : columnIndex === 0 const isFieldName = this.isVerticalTimeAxis ? isFirstRow : isFirstCol
const isFixedCorner = rowIndex === 0 && columnIndex === 0 const isFixedCorner = isFirstRow && isFirstCol
const isNumerical = _.isNumber(cellData) const isNumerical = _.isNumber(cellData)
const isHighlightedRow = let cellStyle: React.CSSProperties = style //tslint:disable-line
rowIndex === parent.props.scrollToRow ||
(rowIndex === hoveredRowIndex && hoveredRowIndex !== 0)
const isHighlightedColumn =
columnIndex === parent.props.scrollToColumn ||
(columnIndex === hoveredColumnIndex && hoveredColumnIndex !== 0)
let cellStyle = style
if ( if (
!isFixedRow && !isFixedRow &&
!isFixedColumn && !isFixedColumn &&
@ -531,21 +566,19 @@ class TableGraph extends Component<Props, State> {
!isTimeData && !isTimeData &&
isNumerical isNumerical
) { ) {
const {bgColor, textColor} = generateThresholdsListHexs({ const thresholdData = {colors, lastValue: cellData, cellType: 'table'}
colors, const {bgColor, textColor} = generateThresholdsListHexs(thresholdData)
lastValue: cellData,
cellType: 'table',
})
cellStyle = { cellStyle = {
...style, ...cellStyle,
backgroundColor: bgColor, backgroundColor: bgColor,
color: textColor, color: textColor,
} }
} }
const foundField = const foundField =
isFieldName && fieldOptions.find(field => field.internalName === cellData) isFieldName &&
fieldOptions.find(({internalName}) => internalName === cellData)
const fieldName = const fieldName =
foundField && (foundField.displayName || foundField.internalName) foundField && (foundField.displayName || foundField.internalName)
@ -553,14 +586,15 @@ class TableGraph extends Component<Props, State> {
'table-graph-cell__fixed-row': isFixedRow, 'table-graph-cell__fixed-row': isFixedRow,
'table-graph-cell__fixed-column': isFixedColumn, 'table-graph-cell__fixed-column': isFixedColumn,
'table-graph-cell__fixed-corner': isFixedCorner, 'table-graph-cell__fixed-corner': isFixedCorner,
'table-graph-cell__highlight-row': isHighlightedRow, 'table-graph-cell__highlight-row':
'table-graph-cell__highlight-column': isHighlightedColumn, rowIndex === parent.props.scrollToRow ||
(rowIndex === hoveredRowIndex && hoveredRowIndex > 0),
'table-graph-cell__highlight-column':
columnIndex === hoveredColumnIndex && hoveredColumnIndex > 0,
'table-graph-cell__numerical': isNumerical, 'table-graph-cell__numerical': isNumerical,
'table-graph-cell__field-name': isFieldName, 'table-graph-cell__field-name': isFieldName,
'table-graph-cell__sort-asc': 'table-graph-cell__sort-asc': isFieldName && isSorted && isAscending,
isFieldName && sort.field === cellData && sort.direction === ASCENDING, 'table-graph-cell__sort-desc': isFieldName && isSorted && !isAscending,
'table-graph-cell__sort-desc':
isFieldName && sort.field === cellData && sort.direction === DESCENDING,
}) })
const cellContents = this.createCellContents( const cellContents = this.createCellContents(
@ -576,11 +610,13 @@ class TableGraph extends Component<Props, State> {
style={cellStyle} style={cellStyle}
className={cellClass} className={cellClass}
onClick={ onClick={
isFieldName && typeof cellData === 'string' isFieldName && _.isString(cellData)
? this.handleClickFieldName(cellData) ? this.handleClickFieldName(cellData)
: null : null
} }
onMouseOver={_.throttle(this.handleHover(columnIndex, rowIndex), 100)} data-column-index={columnIndex}
data-row-index={rowIndex}
onMouseOver={this.handleHover}
title={cellContents} title={cellContents}
> >
{cellContents} {cellContents}