diff --git a/ui/src/dashboards/components/GraphOptionsTimeAxis.js b/ui/src/dashboards/components/GraphOptionsTimeAxis.js index ac6090b00d..e92e3ac295 100644 --- a/ui/src/dashboards/components/GraphOptionsTimeAxis.js +++ b/ui/src/dashboards/components/GraphOptionsTimeAxis.js @@ -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}) =>
-const {func, string} = PropTypes +const {bool, func} = PropTypes -GraphOptionsTimeAxis.propTypes = {TimeAxis: string, onToggleTimeAxis: func} +GraphOptionsTimeAxis.propTypes = { + verticalTimeAxis: bool, + onToggleVerticalTimeAxis: func, +} export default GraphOptionsTimeAxis diff --git a/ui/src/dashboards/components/TableOptions.tsx b/ui/src/dashboards/components/TableOptions.tsx index c8694e4c3e..1a2da1067e 100644 --- a/ui/src/dashboards/components/TableOptions.tsx +++ b/ui/src/dashboards/components/TableOptions.tsx @@ -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 { 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 { ) 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 { handleUpdateTableOptions({...tableOptions, timeFormat}) } - handleToggleTimeAxis = () => {} + onToggleVerticalTimeAxis = verticalTimeAxis => () => { + const {tableOptions, handleUpdateTableOptions} = this.props + handleUpdateTableOptions({...tableOptions, verticalTimeAxis}) + } handleToggleTextWrapping = () => {} @@ -116,16 +118,14 @@ export class TableOptions extends PureComponent { 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 { onTimeFormatChange={this.handleTimeFormatChange} /> { } const mapStateToProps = ({cellEditorOverlay: {cell: {tableOptions}}}) => ({ - tableOptions, + tableOptions }) const mapDispatchToProps = dispatch => ({ - handleUpdateTableOptions: bindActionCreators(updateTableOptions, dispatch), + handleUpdateTableOptions: bindActionCreators(updateTableOptions, dispatch) }) export default connect(mapStateToProps, mapDispatchToProps)(TableOptions) diff --git a/ui/src/dashboards/constants/index.js b/ui/src/dashboards/constants/index.js index 15ee95eed4..20404f3497 100644 --- a/ui/src/dashboards/constants/index.js +++ b/ui/src/dashboards/constants/index.js @@ -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 = { diff --git a/ui/src/dashboards/reducers/cellEditorOverlay.js b/ui/src/dashboards/reducers/cellEditorOverlay.js index 8c4159759d..4629e89750 100644 --- a/ui/src/dashboards/reducers/cellEditorOverlay.js +++ b/ui/src/dashboards/reducers/cellEditorOverlay.js @@ -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, diff --git a/ui/src/shared/components/TableGraph.js b/ui/src/shared/components/TableGraph.js index f8856c97fe..130e9b0af1 100644 --- a/ui/src/shared/components/TableGraph.js +++ b/ui/src/shared/components/TableGraph.js @@ -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} diff --git a/ui/src/shared/constants/tableGraph.js b/ui/src/shared/constants/tableGraph.js index ffa2694f82..e0949b6e0d 100644 --- a/ui/src/shared/constants/tableGraph.js +++ b/ui/src/shared/constants/tableGraph.js @@ -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], diff --git a/ui/src/shared/constants/thresholds.js b/ui/src/shared/constants/thresholds.js index 0d366ebcb5..9941bef092 100644 --- a/ui/src/shared/constants/thresholds.js +++ b/ui/src/shared/constants/thresholds.js @@ -187,3 +187,7 @@ export const validateGaugeColors = colors => { return formattedColors } + +export const stringifyColorValues = colors => { + return colors.map(color => ({...color, value: `${color.value}`})) +} diff --git a/ui/src/utils/timeSeriesToDygraph.js b/ui/src/utils/timeSeriesToDygraph.js index a3a8b0f80a..b5d18c3cc9 100644 --- a/ui/src/utils/timeSeriesToDygraph.js +++ b/ui/src/utils/timeSeriesToDygraph.js @@ -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, } }