Merge pull request #2996 from influxdata/feature/table-graph-series-renaming
Table Graph Series/Column renamingpull/10616/head
commit
27168d8c14
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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',
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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 => {
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
|
@ -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)
|
||||
})
|
||||
})
|
||||
})
|
Loading…
Reference in New Issue