Sort table graphs in cell editor overlay
parent
0587c94345
commit
1c08717148
|
@ -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
|
|
@ -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
|
|
@ -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}
|
||||
/>
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
]
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
})
|
||||
})
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue