Merge pull request #2996 from influxdata/feature/table-graph-series-renaming

Table Graph Series/Column renaming
pull/10616/head
Iris Scholten 2018-03-15 16:27:47 -07:00 committed by GitHub
commit 27168d8c14
8 changed files with 232 additions and 74 deletions

View File

@ -1,34 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import InputClickToEdit from 'shared/components/InputClickToEdit'
const GraphOptionsCustomizableColumn = ({
originalColumnName,
newColumnName,
onColumnRename,
}) => {
return (
<div className="column-controls--section">
<div className="column-controls--label">
{originalColumnName}
</div>
<InputClickToEdit
value={newColumnName}
wrapperClass="column-controls-input"
onBlur={onColumnRename}
placeholder="Rename..."
appearAsNormalInput={true}
/>
</div>
)
}
const {func, string} = PropTypes
GraphOptionsCustomizableColumn.propTypes = {
originalColumnName: string,
newColumnName: string,
onColumnRename: func,
}
export default GraphOptionsCustomizableColumn

View File

@ -0,0 +1,48 @@
import React, {PureComponent} from 'react'
import InputClickToEdit from 'src/shared/components/InputClickToEdit'
type Column = {
internalName: string
displayName: string
}
interface Props {
internalName: string
displayName: string
onColumnRename: (column: Column) => void
}
class GraphOptionsCustomizableColumn extends PureComponent<Props, {}> {
constructor(props) {
super(props)
this.handleColumnRename = this.handleColumnRename.bind(this)
}
handleColumnRename(rename) {
const {onColumnRename, internalName} = this.props
onColumnRename({internalName, displayName: rename})
}
render() {
const {internalName, displayName} = this.props
return (
<div className="column-controls--section">
<div className="column-controls--label">
{internalName}
</div>
<InputClickToEdit
value={displayName}
wrapperClass="column-controls-input"
onBlur={this.handleColumnRename}
placeholder="Rename..."
appearAsNormalInput={true}
/>
</div>
)
}
}
export default GraphOptionsCustomizableColumn

View File

@ -1,10 +1,22 @@
import React from 'react'
import PropTypes from 'prop-types'
import React, {SFC} from 'react'
import GraphOptionsCustomizableColumn from 'src/dashboards/components/GraphOptionsCustomizableColumn'
import uuid from 'uuid'
const GraphOptionsCustomizeColumns = ({columns, onColumnRename}) => {
type Column = {
internalName: string
displayName: string
}
interface Props {
columns: Column[]
onColumnRename: (column: Column) => void
}
const GraphOptionsCustomizeColumns: SFC<Props> = ({
columns,
onColumnRename,
}) => {
return (
<div className="graph-options-group">
<label className="form-label">Customize Columns</label>
@ -12,8 +24,8 @@ const GraphOptionsCustomizeColumns = ({columns, onColumnRename}) => {
return (
<GraphOptionsCustomizableColumn
key={uuid.v4()}
originalColumnName={col.name}
newColumnName={col.newName}
internalName={col.internalName}
displayName={col.displayName}
onColumnRename={onColumnRename}
/>
)
@ -21,16 +33,5 @@ const GraphOptionsCustomizeColumns = ({columns, onColumnRename}) => {
</div>
)
}
const {arrayOf, func, shape, string} = PropTypes
GraphOptionsCustomizeColumns.propTypes = {
columns: arrayOf(
shape({
name: string,
newName: string,
})
),
onColumnRename: func,
}
export default GraphOptionsCustomizeColumns

View File

@ -18,11 +18,7 @@ import {
updateSingleStatColors,
updateTableOptions,
} from 'src/dashboards/actions/cellEditorOverlay'
const formatColor = color => {
const {hex, name} = color
return {hex, name}
}
import {TIME_COLUMN_DEFAULT} from 'src/shared/constants/tableGraph'
type Color = {
type: string
@ -47,10 +43,12 @@ type Options = {
type QueryConfig = {
measurement: string
fields: [{
alias: string
value: string
}]
fields: [
{
alias: string
value: string
}
]
}
interface Props {
@ -63,7 +61,42 @@ interface Props {
tableOptions: Options
}
const formatColor = color => {
const {hex, name} = color
return {hex, name}
}
export class TableOptions extends PureComponent<Props, {}> {
constructor(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
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})
}
handleToggleSingleStatType = () => {}
handleAddThreshold = () => {}
@ -83,7 +116,14 @@ export class TableOptions extends PureComponent<Props, {}> {
handleToggleTextWrapping = () => {}
handleColumnRename = () => {}
handleColumnRename = column => {
const {handleUpdateTableOptions, tableOptions} = this.props
const {columnNames} = tableOptions
const updatedColumns = columnNames.map(
op => (op.internalName === column.internalName ? column : op)
)
handleUpdateTableOptions({...tableOptions, columnNames: updatedColumns})
}
handleUpdateColorValue = () => {}
@ -93,22 +133,13 @@ export class TableOptions extends PureComponent<Props, {}> {
const {
singleStatColors,
singleStatType,
tableOptions: {timeFormat},
tableOptions: {timeFormat, columnNames: columns},
} = this.props
const disableAddThreshold = singleStatColors.length > MAX_THRESHOLDS
const TimeAxis = 'vertical'
const sortedColors = _.sortBy(singleStatColors, color => color.value)
const columns = [
'cpu.mean_usage_system',
'cpu.mean_usage_idle',
'cpu.mean_usage_user',
].map(col => ({
text: col,
name: col,
newName: '',
}))
const tableSortByOptions = [
'cpu.mean_usage_system',
'cpu.mean_usage_idle',

View File

@ -9,6 +9,7 @@ import {
NULL_ROW_INDEX,
NULL_HOVER_TIME,
TIME_FORMAT_DEFAULT,
TIME_COLUMN_DEFAULT,
} from 'src/shared/constants/tableGraph'
import {MultiGrid} from 'react-virtualized'
@ -71,6 +72,9 @@ class TableGraph extends Component {
const timeFormat = tableOptions
? tableOptions.timeFormat
: TIME_FORMAT_DEFAULT
const columnNames = tableOptions
? tableOptions.columnNames
: [TIME_COLUMN_DEFAULT]
const isFixedRow = rowIndex === 0 && columnIndex > 0
const isFixedColumn = rowIndex > 0 && columnIndex === 0
@ -94,6 +98,13 @@ class TableGraph extends Component {
'table-graph-cell__numerical': dataIsNumerical,
})
const cellData = data[rowIndex][columnIndex]
const foundColumn = columnNames.find(
column => column.internalName === cellData
)
const columnName =
foundColumn && (foundColumn.displayName || foundColumn.internalName)
return (
<div
key={key}
@ -102,8 +113,8 @@ class TableGraph extends Component {
onMouseOver={this.handleHover(columnIndex, rowIndex)}
>
{isTimeData
? `${moment(data[rowIndex][columnIndex]).format(timeFormat)}`
: `${data[rowIndex][columnIndex]}`}
? `${moment(cellData).format(timeFormat)}`
: columnName || `${cellData}`}
</div>
)
}
@ -141,6 +152,9 @@ class TableGraph extends Component {
timeFormat={
tableOptions ? tableOptions.timeFormat : TIME_FORMAT_DEFAULT
}
columnNames={
tableOptions ? tableOptions.columnNames : [TIME_COLUMN_DEFAULT]
}
scrollToRow={hoverTimeRow}
cellRenderer={this.cellRenderer}
hoveredColumnIndex={hoveredColumnIndex}

View File

@ -6,6 +6,8 @@ export const NULL_HOVER_TIME = '0'
export const TIME_FORMAT_DEFAULT = 'MM/DD/YYYY HH:mm:ss.ss'
export const TIME_FORMAT_CUSTOM = 'Custom'
export const TIME_COLUMN_DEFAULT = {internalName: 'time', displayName: ''}
export const FORMAT_OPTIONS = [
{text: TIME_FORMAT_DEFAULT},
{text: 'MM/DD/YYYY HH:mm'},
@ -21,9 +23,9 @@ export const FORMAT_OPTIONS = [
export const DEFAULT_TABLE_OPTIONS = {
timeFormat: 'MM/DD/YYYY HH:mm:ss.ss',
verticalTimeAxis: true,
sortBy: {internalName: 'time', displayName: ''},
sortBy: TIME_COLUMN_DEFAULT,
wrapping: 'truncate',
columnNames: [{internalName: 'time', displayName: ''}],
columnNames: [TIME_COLUMN_DEFAULT],
}
export const initializeOptions = cellType => {

View File

@ -0,0 +1,62 @@
import React from 'react'
import GraphOptionsCustomizableColumn from 'src/dashboards/components/GraphOptionsCustomizableColumn'
import InputClickToEdit from 'src/shared/components/InputClickToEdit'
import {shallow} from 'enzyme'
const setup = (override = {}) => {
const props = {
internalName: '',
displayName: '',
onColumnRename: () => {},
...override,
}
const wrapper = shallow(<GraphOptionsCustomizableColumn {...props} />)
const instance = wrapper.instance() as GraphOptionsCustomizableColumn
return {wrapper, props, instance}
}
describe('Dashboards.Components.GraphOptionsCustomizableColumn', () => {
describe('rendering', () => {
it('displays both label div and InputClickToEdit', () => {
const {wrapper} = setup()
const label = wrapper.find('div').last()
const input = wrapper.find(InputClickToEdit)
expect(label.exists()).toBe(true)
expect(input.exists()).toBe(true)
})
describe('when there is an internalName', () => {
it('displays the value', () => {
const internalName = 'test'
const {wrapper} = setup({internalName})
const label = wrapper.find('div').last()
expect(label.exists()).toBe(true)
expect(label.children().contains(internalName)).toBe(true)
})
})
})
describe('instance methods', () => {
describe('#handleColumnRename', () => {
it('calls onColumnRename once', () => {
const onColumnRename = jest.fn()
const internalName = 'test'
const {instance} = setup({onColumnRename, internalName})
const rename = 'TEST'
instance.handleColumnRename(rename)
expect(onColumnRename).toHaveBeenCalledTimes(1)
expect(onColumnRename).toHaveBeenCalledWith({
internalName,
displayName: rename,
})
})
})
})
})

View File

@ -0,0 +1,34 @@
import React from 'react'
import GraphOptionsCustomizeColumns from 'src/dashboards/components/GraphOptionsCustomizeColumns'
import GraphOptionsCustomizableColumn from 'src/dashboards/components/GraphOptionsCustomizableColumn'
import {TIME_COLUMN_DEFAULT} from 'src/shared/constants/tableGraph'
import {shallow} from 'enzyme'
const setup = (override = {}) => {
const props = {
columns: [],
onColumnRename: () => {},
...override,
}
const wrapper = shallow(<GraphOptionsCustomizeColumns {...props} />)
return {wrapper, props}
}
describe('Dashboards.Components.GraphOptionsCustomizeColumns', () => {
describe('rendering', () => {
it('displays label and all columns passed in', () => {
const columns = [TIME_COLUMN_DEFAULT]
const {wrapper} = setup({columns})
const label = wrapper.find('label')
const customizableColumns = wrapper.find(GraphOptionsCustomizableColumn)
expect(label.exists()).toBe(true)
expect(customizableColumns.exists()).toBe(true)
expect(customizableColumns.length).toBe(columns.length)
})
})
})