Sort table graphs in cell editor overlay

pull/3005/head
Brandon Farmer 2018-03-16 17:41:28 -07:00
parent 0587c94345
commit 1c08717148
7 changed files with 241 additions and 101 deletions

View File

@ -1,29 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import Dropdown from 'shared/components/Dropdown'
const GraphOptionsSortBy = ({sortByOptions, onChooseSortBy}) =>
<div className="form-group col-xs-6">
<label>Sort By</label>
<Dropdown
items={sortByOptions}
selected={sortByOptions[0].text}
buttonColor="btn-default"
buttonSize="btn-sm"
className="dropdown-stretch"
onChoose={onChooseSortBy}
/>
</div>
const {arrayOf, func, shape, string} = PropTypes
GraphOptionsSortBy.propTypes = {
sortByOptions: arrayOf(
shape({
text: string.isRequired,
}).isRequired
),
onChooseSortBy: func,
}
export default GraphOptionsSortBy

View File

@ -0,0 +1,24 @@
import React from 'react'
import Dropdown from 'src/shared/components/Dropdown'
interface Props {
sortByOptions: any[]
onChooseSortBy: (any) => void
selected: string
}
const GraphOptionsSortBy = ({sortByOptions, onChooseSortBy, selected} : Props) => (
<div className="form-group col-xs-6">
<label>Sort By</label>
<Dropdown
items={sortByOptions}
selected={selected}
buttonColor="btn-default"
buttonSize="btn-sm"
className="dropdown-stretch"
onChoose={onChooseSortBy}
/>
</div>
)
export default GraphOptionsSortBy

View File

@ -29,14 +29,12 @@ type Options = {
columnNames: TableColumn[]
}
type QueryConfig = {
interface QueryConfig {
measurement: string
fields: [
{
alias: string
value: string
}
]
fields: {
alias: string
value: string
}[]
}
interface Props {
@ -51,33 +49,52 @@ export class TableOptions extends PureComponent<Props, {}> {
super(props)
}
componentWillMount() {
const {queryConfigs, handleUpdateTableOptions, tableOptions} = this.props
const {columnNames} = tableOptions
const timeColumn =
(columnNames && columnNames.find(c => c.internalName === 'time')) ||
TIME_COLUMN_DEFAULT
get columnNames() {
const {tableOptions: {columnNames}} = this.props
const columns = [
timeColumn,
..._.flatten(
queryConfigs.map(qc => {
const {measurement, fields} = qc
return fields.map(f => {
const internalName = `${measurement}.${f.alias}`
const existing = columnNames.find(
c => c.internalName === internalName
)
return existing || {internalName, displayName: ''}
})
})
),
]
handleUpdateTableOptions({...tableOptions, columnNames: columns})
return columnNames || []
}
handleChooseSortBy = () => {}
get timeColumn() {
return (this.columnNames.find(c => c.internalName === 'time')) || TIME_COLUMN_DEFAULT
}
get computedColumnNames() {
const {queryConfigs} = this.props
const queryFields = _.flatten(
queryConfigs.map(({measurement, fields}) => {
return fields.map(({alias}) => {
const internalName = `${measurement}.${alias}`
const existing = this.columnNames.find(
c => c.internalName === internalName
)
return existing || {internalName, displayName: ''}
})
}))
return [this.timeColumn, ...queryFields]
}
componentWillMount() {
const {handleUpdateTableOptions, tableOptions} = this.props
handleUpdateTableOptions({...tableOptions, columnNames: this.computedColumnNames})
}
handleToggleSingleStatType = () => {}
handleAddThreshold = () => {}
handleDeleteThreshold = () => () => {}
handleChooseColor = () => () => {}
handleChooseSortBy = option => {
const {tableOptions, handleUpdateTableOptions} = this.props
const sortBy = {displayName: option.text, internalName: option.key}
handleUpdateTableOptions({...tableOptions, sortBy})
}
handleTimeFormatChange = timeFormat => {
const {tableOptions, handleUpdateTableOptions} = this.props
@ -101,15 +118,15 @@ export class TableOptions extends PureComponent<Props, {}> {
const {
tableOptions: {timeFormat, columnNames: columns},
onResetFocus,
tableOptions,
} = this.props
const TimeAxis = 'vertical'
const tableSortByOptions = [
'cpu.mean_usage_system',
'cpu.mean_usage_idle',
'cpu.mean_usage_user',
].map(col => ({text: col}))
const tableSortByOptions = this.computedColumnNames.map(col => ({
text: col.displayName || col.internalName,
key: col.internalName,
}))
return (
<FancyScrollbar
@ -128,6 +145,7 @@ export class TableOptions extends PureComponent<Props, {}> {
onToggleTimeAxis={this.handleToggleTimeAxis}
/>
<GraphOptionsSortBy
selected={tableOptions.sortBy.internalName || TIME_COLUMN_DEFAULT.internalName}
sortByOptions={tableSortByOptions}
onChooseSortBy={this.handleChooseSortBy}
/>

View File

@ -546,9 +546,9 @@ export const GRAPH_TYPES = [
graphic: GRAPH_SVGS.gauge,
},
// FEATURE FLAG for Table-Graph
// {
// type: 'table',
// menuOption: 'Table',
// graphic: GRAPH_SVGS.table,
// },
{
type: 'table',
menuOption: 'Table',
graphic: GRAPH_SVGS.table,
},
]

View File

@ -2,6 +2,7 @@ import React, {Component} from 'react'
import PropTypes from 'prop-types'
import _ from 'lodash'
import classnames from 'classnames'
import isEmpty from 'lodash/isEmpty'
import {MultiGrid} from 'react-virtualized'
import moment from 'moment'
@ -16,24 +17,25 @@ import {
} from 'src/shared/constants/tableGraph'
import {generateThresholdsListHexs} from 'shared/constants/colorOperations'
const isEmpty = data => data.length <= 1
class TableGraph extends Component {
constructor(props) {
super(props)
this.state = {
data: [[]],
hoveredColumnIndex: NULL_COLUMN_INDEX,
hoveredRowIndex: NULL_ROW_INDEX,
columnIndex: -1,
}
}
componentWillMount() {
this._data = [[]]
}
componentWillUpdate(nextProps) {
componentWillReceiveProps(nextProps) {
const {data} = timeSeriesToTableGraph(nextProps.data)
this._data = data
const {tableOptions: {sortBy: {internalName}}} = nextProps
const columnIndex = _.indexOf(data[0], internalName)
const sortedData = _.sortBy(_.drop(data, 1), columnIndex)
this.setState({data: [data[0], ...sortedData], columnIndex})
}
calcHoverTimeRow = (data, hoverTime) =>
@ -45,7 +47,8 @@ class TableGraph extends Component {
handleHover = (columnIndex, rowIndex) => () => {
if (this.props.onSetHoverTime) {
this.props.onSetHoverTime(this._data[rowIndex][0].toString())
const {data} = this.state
this.props.onSetHoverTime(data[rowIndex][0].toString())
this.setState({
hoveredColumnIndex: columnIndex,
hoveredRowIndex: rowIndex,
@ -63,20 +66,17 @@ class TableGraph extends Component {
}
}
cellRenderer = ({columnIndex, rowIndex, key, parent, style}) => {
const data = this._data
const {hoveredColumnIndex, hoveredRowIndex} = this.state
cellRenderer = ({columnIndex, rowIndex, key, style, parent}) => {
const {hoveredColumnIndex, hoveredRowIndex, data} = this.state
const {colors} = this.props
const columnCount = _.get(data, ['0', 'length'], 0)
const rowCount = data.length
const {tableOptions} = this.props
const timeFormat = tableOptions
? tableOptions.timeFormat
: TIME_FORMAT_DEFAULT
const columnNames = tableOptions
? tableOptions.columnNames
: [TIME_COLUMN_DEFAULT]
const timeFormat = _.get(tableOptions, 'timeFormat', TIME_FORMAT_DEFAULT)
const columnNames = _.get(tableOptions, 'columnNames', [
TIME_COLUMN_DEFAULT,
])
const isFixedRow = rowIndex === 0 && columnIndex > 0
const isFixedColumn = rowIndex > 0 && columnIndex === 0
@ -139,14 +139,17 @@ class TableGraph extends Component {
render() {
const {hoveredColumnIndex, hoveredRowIndex} = this.state
const {hoverTime, tableOptions, colors} = this.props
const data = this._data
const {data} = this.state
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 = this.calcHoverTimeRow(data, hoverTime)
const hoverTimeRow =
hoveredRowIndex === NULL_ROW_INDEX
? this.calcHoverTimeRow(data, hoverTime)
: hoveredRowIndex
return (
<div
@ -172,6 +175,7 @@ class TableGraph extends Component {
columnNames={
tableOptions ? tableOptions.columnNames : [TIME_COLUMN_DEFAULT]
}
columnIndex={columnIndex}
scrollToRow={hoverTimeRow}
cellRenderer={this.cellRenderer}
hoveredColumnIndex={hoveredColumnIndex}

View File

@ -0,0 +1,32 @@
import React from 'react'
import {shallow} from 'enzyme'
import GraphOptionsSortBy from 'src/dashboards/components/GraphOptionsSortBy'
import Dropdown from 'src/shared/components/Dropdown'
const defaultProps = {
sortByOptions: [],
onChooseSortBy: () => {},
selected: ''
}
const setup = (override = {}) => {
const props = {...defaultProps, ...override}
const wrapper = shallow(<GraphOptionsSortBy {...props} />)
return {wrapper, props}
}
describe('Dashboards.Components.GraphOptionsSortBy', () => {
describe('rendering', () => {
it('renders component', () => {
const {wrapper} = setup()
const dropdown = wrapper.find(Dropdown)
const label = wrapper.find('label')
expect(dropdown.exists()).toBe(true)
expect(label.exists()).toBe(true)
})
})
})

View File

@ -13,18 +13,51 @@ import ThresholdsListTypeToggle from 'src/shared/components/ThresholdsListTypeTo
import {shallow} from 'enzyme'
const queryConfigs = [
{
measurement: "dev",
fields: [
{
alias: "boom",
value: "test"
},
{
alias: "again",
value: "again"
},
]
},
{
measurement: "prod",
fields: [
{
alias: "boom",
value: "test"
},
{
alias: "again",
value: "again"
},
]
}
]
const defaultProps = {
queryConfigs: queryConfigs,
handleUpdateTableOptions: () => {},
tableOptions: {
timeFormat: '',
verticalTimeAxis: true,
sortBy: {internalName: '', displayName: ''},
wrapping: '',
columnNames: [],
},
onResetFocus: () => {},
}
const setup = (override = {}) => {
const props = {
queryConfigs: [],
handleUpdateTableOptions: () => {},
tableOptions: {
timeFormat: '',
verticalTimeAxis: true,
sortBy: {internalName: '', displayName: ''},
wrapping: '',
columnNames: [],
},
onResetFocus: () => {},
...defaultProps,
...override,
}
@ -34,9 +67,65 @@ const setup = (override = {}) => {
}
describe('Dashboards.Components.TableOptions', () => {
describe('getters', () => {
describe('computedColumnNames', () => {
it('returns the correct column names', () => {
const instance = new TableOptions(defaultProps)
const expected = [
{
displayName: '',
internalName: 'time',
},
{
displayName: '',
internalName: 'dev.boom',
},
{
displayName: '',
internalName: 'dev.again',
},
{
displayName: '',
internalName: 'prod.boom',
},
{
displayName: '',
internalName: 'prod.again',
},
]
expect(instance.computedColumnNames).toEqual(expected)
})
})
})
describe('rendering', () => {
it('should render all components', () => {
const {wrapper} = setup()
const queryConfigs = [
{
measurement: "dev",
fields: [
{
alias: "boom",
value: "test"
},
]
}
]
const expectedSortOptions = [
{
key: 'time',
text: 'time',
},
{
key: 'dev.boom',
text: 'dev.boom',
},
]
const {wrapper} = setup({ queryConfigs })
const fancyScrollbar = wrapper.find(FancyScrollbar)
const graphOptionsTimeFormat = wrapper.find(GraphOptionsTimeFormat)
const graphOptionsTimeAxis = wrapper.find(GraphOptionsTimeAxis)
@ -48,6 +137,8 @@ describe('Dashboards.Components.TableOptions', () => {
const thresholdsList = wrapper.find(ThresholdsList)
const thresholdsListTypeToggle = wrapper.find(ThresholdsListTypeToggle)
expect(graphOptionsSortBy.props().sortByOptions).toEqual(expectedSortOptions)
expect(fancyScrollbar.exists()).toBe(true)
expect(graphOptionsTimeFormat.exists()).toBe(true)
expect(graphOptionsTimeAxis.exists()).toBe(true)