Merge pull request #3010 from influxdata/feature/table-graph-time-axis
Feature/table graph time axispull/10616/head
commit
d179d6f723
|
@ -1,29 +1,30 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
const VERTICAL = 'VERTICAL'
|
||||
const HORIZONTAL = 'HORIZONTAL'
|
||||
const GraphOptionsTimeAxis = ({TimeAxis, onToggleTimeAxis}) =>
|
||||
const GraphOptionsTimeAxis = ({verticalTimeAxis, onToggleVerticalTimeAxis}) =>
|
||||
<div className="form-group col-xs-12 col-sm-6">
|
||||
<label>Time Axis</label>
|
||||
<ul className="nav nav-tablist nav-tablist-sm">
|
||||
<li
|
||||
className={`${TimeAxis === VERTICAL ? 'active' : ''}`}
|
||||
onClick={onToggleTimeAxis}
|
||||
className={verticalTimeAxis ? 'active' : ''}
|
||||
onClick={onToggleVerticalTimeAxis(true)}
|
||||
>
|
||||
Vertical
|
||||
</li>
|
||||
<li
|
||||
className={`${TimeAxis === HORIZONTAL ? 'active' : ''}`}
|
||||
onClick={onToggleTimeAxis}
|
||||
className={verticalTimeAxis ? '' : 'active'}
|
||||
onClick={onToggleVerticalTimeAxis(false)}
|
||||
>
|
||||
Horizontal
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
const {func, string} = PropTypes
|
||||
const {bool, func} = PropTypes
|
||||
|
||||
GraphOptionsTimeAxis.propTypes = {TimeAxis: string, onToggleTimeAxis: func}
|
||||
GraphOptionsTimeAxis.propTypes = {
|
||||
verticalTimeAxis: bool,
|
||||
onToggleVerticalTimeAxis: func,
|
||||
}
|
||||
|
||||
export default GraphOptionsTimeAxis
|
||||
|
|
|
@ -10,6 +10,7 @@ import GraphOptionsTimeAxis from 'src/dashboards/components/GraphOptionsTimeAxis
|
|||
import GraphOptionsSortBy from 'src/dashboards/components/GraphOptionsSortBy'
|
||||
import GraphOptionsTextWrapping from 'src/dashboards/components/GraphOptionsTextWrapping'
|
||||
import GraphOptionsCustomizeColumns from 'src/dashboards/components/GraphOptionsCustomizeColumns'
|
||||
|
||||
import ThresholdsList from 'src/shared/components/ThresholdsList'
|
||||
import ThresholdsListTypeToggle from 'src/shared/components/ThresholdsListTypeToggle'
|
||||
|
||||
|
@ -51,12 +52,14 @@ export class TableOptions extends PureComponent<Props, {}> {
|
|||
|
||||
get columnNames() {
|
||||
const {tableOptions: {columnNames}} = this.props
|
||||
|
||||
return columnNames || []
|
||||
}
|
||||
|
||||
get timeColumn() {
|
||||
return (this.columnNames.find(c => c.internalName === 'time')) || TIME_COLUMN_DEFAULT
|
||||
return (
|
||||
this.columnNames.find(c => c.internalName === 'time') ||
|
||||
TIME_COLUMN_DEFAULT
|
||||
)
|
||||
}
|
||||
|
||||
get computedColumnNames() {
|
||||
|
@ -71,24 +74,20 @@ export class TableOptions extends PureComponent<Props, {}> {
|
|||
)
|
||||
return existing || {internalName, displayName: ''}
|
||||
})
|
||||
}))
|
||||
})
|
||||
)
|
||||
|
||||
return [this.timeColumn, ...queryFields]
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
const {handleUpdateTableOptions, tableOptions} = this.props
|
||||
handleUpdateTableOptions({...tableOptions, columnNames: this.computedColumnNames})
|
||||
handleUpdateTableOptions({
|
||||
...tableOptions,
|
||||
columnNames: this.computedColumnNames
|
||||
})
|
||||
}
|
||||
|
||||
handleToggleSingleStatType = () => {}
|
||||
|
||||
handleAddThreshold = () => {}
|
||||
|
||||
handleDeleteThreshold = () => () => {}
|
||||
|
||||
handleChooseColor = () => () => {}
|
||||
|
||||
handleChooseSortBy = option => {
|
||||
const {tableOptions, handleUpdateTableOptions} = this.props
|
||||
const sortBy = {displayName: option.text, internalName: option.key}
|
||||
|
@ -101,7 +100,10 @@ export class TableOptions extends PureComponent<Props, {}> {
|
|||
handleUpdateTableOptions({...tableOptions, timeFormat})
|
||||
}
|
||||
|
||||
handleToggleTimeAxis = () => {}
|
||||
onToggleVerticalTimeAxis = verticalTimeAxis => () => {
|
||||
const {tableOptions, handleUpdateTableOptions} = this.props
|
||||
handleUpdateTableOptions({...tableOptions, verticalTimeAxis})
|
||||
}
|
||||
|
||||
handleToggleTextWrapping = () => {}
|
||||
|
||||
|
@ -116,16 +118,14 @@ export class TableOptions extends PureComponent<Props, {}> {
|
|||
|
||||
render() {
|
||||
const {
|
||||
tableOptions: {timeFormat, columnNames: columns},
|
||||
tableOptions: {timeFormat, columnNames: columns, verticalTimeAxis},
|
||||
onResetFocus,
|
||||
tableOptions,
|
||||
tableOptions
|
||||
} = this.props
|
||||
|
||||
const TimeAxis = 'vertical'
|
||||
|
||||
const tableSortByOptions = this.computedColumnNames.map(col => ({
|
||||
text: col.displayName || col.internalName,
|
||||
key: col.internalName,
|
||||
key: col.internalName
|
||||
}))
|
||||
|
||||
return (
|
||||
|
@ -141,8 +141,8 @@ export class TableOptions extends PureComponent<Props, {}> {
|
|||
onTimeFormatChange={this.handleTimeFormatChange}
|
||||
/>
|
||||
<GraphOptionsTimeAxis
|
||||
TimeAxis={TimeAxis}
|
||||
onToggleTimeAxis={this.handleToggleTimeAxis}
|
||||
verticalTimeAxis={verticalTimeAxis}
|
||||
onToggleVerticalTimeAxis={this.onToggleVerticalTimeAxis}
|
||||
/>
|
||||
<GraphOptionsSortBy
|
||||
selected={tableOptions.sortBy || TIME_COLUMN_DEFAULT}
|
||||
|
@ -169,11 +169,11 @@ export class TableOptions extends PureComponent<Props, {}> {
|
|||
}
|
||||
|
||||
const mapStateToProps = ({cellEditorOverlay: {cell: {tableOptions}}}) => ({
|
||||
tableOptions,
|
||||
tableOptions
|
||||
})
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
handleUpdateTableOptions: bindActionCreators(updateTableOptions, dispatch),
|
||||
handleUpdateTableOptions: bindActionCreators(updateTableOptions, dispatch)
|
||||
})
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(TableOptions)
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import {DEFAULT_TABLE_OPTIONS} from 'src/shared/constants/tableGraph'
|
||||
|
||||
export const EMPTY_DASHBOARD = {
|
||||
id: 0,
|
||||
name: '',
|
||||
|
@ -20,6 +22,7 @@ export const NEW_DEFAULT_DASHBOARD_CELL = {
|
|||
name: 'Untitled Cell',
|
||||
type: 'line',
|
||||
queries: [],
|
||||
tableOptions: DEFAULT_TABLE_OPTIONS,
|
||||
}
|
||||
|
||||
export const NEW_DASHBOARD = {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import _ from 'lodash'
|
||||
|
||||
import {
|
||||
THRESHOLD_TYPE_TEXT,
|
||||
DEFAULT_THRESHOLDS_LIST_COLORS,
|
||||
|
@ -28,7 +30,11 @@ export default function cellEditorOverlay(state = initialState, action) {
|
|||
)
|
||||
const gaugeColors = validateGaugeColors(colors)
|
||||
|
||||
const tableOptions = cell.tableOptions || initializeOptions('table')
|
||||
const tableOptions = _.get(
|
||||
cell,
|
||||
'tableOptions',
|
||||
initializeOptions('table')
|
||||
)
|
||||
|
||||
return {
|
||||
...state,
|
||||
|
|
|
@ -22,6 +22,7 @@ class TableGraph extends Component {
|
|||
super(props)
|
||||
this.state = {
|
||||
data: [[]],
|
||||
unzippedData: [[]],
|
||||
hoveredColumnIndex: NULL_COLUMN_INDEX,
|
||||
hoveredRowIndex: NULL_ROW_INDEX,
|
||||
sortByColumnIndex: -1,
|
||||
|
@ -29,31 +30,44 @@ class TableGraph extends Component {
|
|||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const {data} = timeSeriesToTableGraph(nextProps.data)
|
||||
const {data, unzippedData} = timeSeriesToTableGraph(nextProps.data)
|
||||
|
||||
const {tableOptions: {sortBy: {internalName}}} = nextProps
|
||||
const sortByColumnIndex = _.indexOf(data[0], internalName)
|
||||
|
||||
const sortedData = _.sortBy(_.drop(data, 1), sortByColumnIndex)
|
||||
this.setState({data: [data[0], ...sortedData], sortByColumnIndex})
|
||||
this.setState({
|
||||
data: [data[0], ...sortedData],
|
||||
unzippedData,
|
||||
sortByColumnIndex,
|
||||
})
|
||||
}
|
||||
|
||||
calcHoverTimeRow = (data, hoverTime) =>
|
||||
!isEmpty(data) && hoverTime !== NULL_HOVER_TIME
|
||||
? data.findIndex(
|
||||
row => row[0] && _.isNumber(row[0]) && row[0] >= hoverTime
|
||||
)
|
||||
: undefined
|
||||
calcHoverTimeIndex = (data, hoverTime, verticalTimeAxis) => {
|
||||
if (isEmpty(data) || hoverTime === NULL_HOVER_TIME) {
|
||||
return undefined
|
||||
}
|
||||
if (verticalTimeAxis) {
|
||||
return data.findIndex(
|
||||
row => row[0] && _.isNumber(row[0]) && row[0] >= hoverTime
|
||||
)
|
||||
}
|
||||
return data[0].findIndex(d => _.isNumber(d) && d >= hoverTime)
|
||||
}
|
||||
|
||||
handleHover = (columnIndex, rowIndex) => () => {
|
||||
if (this.props.onSetHoverTime) {
|
||||
const {data} = this.state
|
||||
this.props.onSetHoverTime(data[rowIndex][0].toString())
|
||||
this.setState({
|
||||
hoveredColumnIndex: columnIndex,
|
||||
hoveredRowIndex: rowIndex,
|
||||
})
|
||||
const {onSetHoverTime, tableOptions} = this.props
|
||||
const {data} = this.state
|
||||
if (onSetHoverTime) {
|
||||
const hoverTime = tableOptions.verticalTimeAxis
|
||||
? data[rowIndex][0]
|
||||
: data[0][columnIndex]
|
||||
onSetHoverTime(hoverTime.toString())
|
||||
}
|
||||
this.setState({
|
||||
hoveredColumnIndex: columnIndex,
|
||||
hoveredRowIndex: rowIndex,
|
||||
})
|
||||
}
|
||||
|
||||
handleMouseOut = () => {
|
||||
|
@ -66,10 +80,12 @@ class TableGraph extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
cellRenderer = ({columnIndex, rowIndex, key, style, parent}) => {
|
||||
const {hoveredColumnIndex, hoveredRowIndex, data} = this.state
|
||||
cellRenderer = ({columnIndex, rowIndex, key, parent, style}) => {
|
||||
const data = _.get(this.props, ['tableOptions', 'verticalTimeAxis'], true)
|
||||
? this.state.data
|
||||
: this.state.unzippedData
|
||||
const {hoveredColumnIndex, hoveredRowIndex} = this.state
|
||||
const {colors} = this.props
|
||||
|
||||
const columnCount = _.get(data, ['0', 'length'], 0)
|
||||
const rowCount = data.length
|
||||
const {tableOptions} = this.props
|
||||
|
@ -80,12 +96,15 @@ class TableGraph extends Component {
|
|||
|
||||
const isFixedRow = rowIndex === 0 && columnIndex > 0
|
||||
const isFixedColumn = rowIndex > 0 && columnIndex === 0
|
||||
const isTimeData = isFixedColumn
|
||||
const isTimeData = tableOptions.verticalTimeAxis
|
||||
? isFixedColumn
|
||||
: isFixedRow
|
||||
const isFixedCorner = rowIndex === 0 && columnIndex === 0
|
||||
const isLastRow = rowIndex === rowCount - 1
|
||||
const isLastColumn = columnIndex === columnCount - 1
|
||||
const isHighlighted =
|
||||
rowIndex === parent.props.scrollToRow ||
|
||||
columnIndex === parent.props.scrollToColumn ||
|
||||
(rowIndex === hoveredRowIndex && hoveredRowIndex !== 0) ||
|
||||
(columnIndex === hoveredColumnIndex && hoveredColumnIndex !== 0)
|
||||
const dataIsNumerical = _.isNumber(data[rowIndex][columnIndex])
|
||||
|
@ -139,16 +158,20 @@ class TableGraph extends Component {
|
|||
render() {
|
||||
const {sortByColumnIndex, hoveredColumnIndex, hoveredRowIndex} = this.state
|
||||
const {hoverTime, tableOptions, colors} = this.props
|
||||
const {data} = this.state
|
||||
|
||||
const verticalTimeAxis = _.get(tableOptions, 'verticalTimeAxis', true)
|
||||
|
||||
const data = verticalTimeAxis ? this.state.data : this.state.unzippedData
|
||||
|
||||
const columnCount = _.get(data, ['0', 'length'], 0)
|
||||
const rowCount = data.length
|
||||
const COLUMN_WIDTH = 300
|
||||
const ROW_HEIGHT = 30
|
||||
const tableWidth = this.gridContainer ? this.gridContainer.clientWidth : 0
|
||||
const tableHeight = this.gridContainer ? this.gridContainer.clientHeight : 0
|
||||
const hoverTimeRow =
|
||||
const hoverTimeIndex =
|
||||
hoveredRowIndex === NULL_ROW_INDEX
|
||||
? this.calcHoverTimeRow(data, hoverTime)
|
||||
? this.calcHoverTimeIndex(data, hoverTime, verticalTimeAxis)
|
||||
: hoveredRowIndex
|
||||
|
||||
return (
|
||||
|
@ -175,8 +198,10 @@ class TableGraph extends Component {
|
|||
columnNames={
|
||||
tableOptions ? tableOptions.columnNames : [TIME_COLUMN_DEFAULT]
|
||||
}
|
||||
scrollToRow={verticalTimeAxis ? hoverTimeIndex : undefined}
|
||||
scrollToColumn={verticalTimeAxis ? undefined : hoverTimeIndex}
|
||||
verticalTimeAxis={verticalTimeAxis}
|
||||
sortByColumnIndex={sortByColumnIndex}
|
||||
scrollToRow={hoverTimeRow}
|
||||
cellRenderer={this.cellRenderer}
|
||||
hoveredColumnIndex={hoveredColumnIndex}
|
||||
hoveredRowIndex={hoveredRowIndex}
|
||||
|
|
|
@ -21,8 +21,8 @@ export const FORMAT_OPTIONS = [
|
|||
]
|
||||
|
||||
export const DEFAULT_TABLE_OPTIONS = {
|
||||
timeFormat: 'MM/DD/YYYY HH:mm:ss.ss',
|
||||
verticalTimeAxis: true,
|
||||
timeFormat: TIME_FORMAT_DEFAULT,
|
||||
sortBy: TIME_COLUMN_DEFAULT,
|
||||
wrapping: 'truncate',
|
||||
columnNames: [TIME_COLUMN_DEFAULT],
|
||||
|
|
|
@ -187,3 +187,7 @@ export const validateGaugeColors = colors => {
|
|||
|
||||
return formattedColors
|
||||
}
|
||||
|
||||
export const stringifyColorValues = colors => {
|
||||
return colors.map(color => ({...color, value: `${color.value}`}))
|
||||
}
|
||||
|
|
|
@ -177,9 +177,11 @@ export const timeSeriesToTableGraph = raw => {
|
|||
const labels = ['time', ...map(sortedLabels, ({label}) => label)]
|
||||
|
||||
const tableData = map(sortedTimeSeries, ({time, values}) => [time, ...values])
|
||||
|
||||
const data = tableData.length ? [labels, ...tableData] : [[]]
|
||||
const unzippedData = _.unzip(data)
|
||||
return {
|
||||
data: tableData.length ? [labels, ...tableData] : [[]],
|
||||
data,
|
||||
unzippedData,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue