Merge pull request #2905 from influxdata/feature/graph-table
TableGraph base functionalitypull/10616/head
commit
119d4f208b
|
@ -132,6 +132,7 @@
|
||||||
"react-router": "^3.0.2",
|
"react-router": "^3.0.2",
|
||||||
"react-router-redux": "^4.0.8",
|
"react-router-redux": "^4.0.8",
|
||||||
"react-tooltip": "^3.2.1",
|
"react-tooltip": "^3.2.1",
|
||||||
|
"react-virtualized": "^9.18.5",
|
||||||
"redux": "^3.3.1",
|
"redux": "^3.3.1",
|
||||||
"redux-auth-wrapper": "^1.0.0",
|
"redux-auth-wrapper": "^1.0.0",
|
||||||
"redux-thunk": "^1.0.3",
|
"redux-thunk": "^1.0.3",
|
||||||
|
|
|
@ -5,6 +5,7 @@ import GraphTypeSelector from 'src/dashboards/components/GraphTypeSelector'
|
||||||
import GaugeOptions from 'src/dashboards/components/GaugeOptions'
|
import GaugeOptions from 'src/dashboards/components/GaugeOptions'
|
||||||
import SingleStatOptions from 'src/dashboards/components/SingleStatOptions'
|
import SingleStatOptions from 'src/dashboards/components/SingleStatOptions'
|
||||||
import AxesOptions from 'src/dashboards/components/AxesOptions'
|
import AxesOptions from 'src/dashboards/components/AxesOptions'
|
||||||
|
import TableOptions from 'src/dashboards/components/TableOptions'
|
||||||
|
|
||||||
import {buildDefaultYLabel} from 'shared/presenters'
|
import {buildDefaultYLabel} from 'shared/presenters'
|
||||||
|
|
||||||
|
@ -46,6 +47,8 @@ class DisplayOptions extends Component {
|
||||||
return <GaugeOptions onResetFocus={onResetFocus} />
|
return <GaugeOptions onResetFocus={onResetFocus} />
|
||||||
case 'single-stat':
|
case 'single-stat':
|
||||||
return <SingleStatOptions onResetFocus={onResetFocus} />
|
return <SingleStatOptions onResetFocus={onResetFocus} />
|
||||||
|
case 'table':
|
||||||
|
return <TableOptions />
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<AxesOptions
|
<AxesOptions
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
import React, {PropTypes} from 'react'
|
||||||
|
|
||||||
|
import InputClickToEdit from 'shared/components/InputClickToEdit'
|
||||||
|
|
||||||
|
const GraphOptionsCustomizableColumn = ({
|
||||||
|
originalColumnName,
|
||||||
|
newColumnName,
|
||||||
|
onColumnRename,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div className="gauge-controls--section">
|
||||||
|
<div className="gauge-controls--label">
|
||||||
|
{originalColumnName}
|
||||||
|
</div>
|
||||||
|
<InputClickToEdit
|
||||||
|
value={newColumnName}
|
||||||
|
wrapperClass="fancytable--td orgs-table--name"
|
||||||
|
onUpdate={onColumnRename}
|
||||||
|
placeholder="Rename..."
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
const {func, string} = PropTypes
|
||||||
|
|
||||||
|
GraphOptionsCustomizableColumn.propTypes = {
|
||||||
|
originalColumnName: string,
|
||||||
|
newColumnName: string,
|
||||||
|
onColumnRename: func,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GraphOptionsCustomizableColumn
|
|
@ -0,0 +1,35 @@
|
||||||
|
import React, {PropTypes} from 'react'
|
||||||
|
|
||||||
|
import GraphOptionsCustomizableColumn from 'src/dashboards/components/GraphOptionsCustomizableColumn'
|
||||||
|
import uuid from 'uuid'
|
||||||
|
|
||||||
|
const GraphOptionsCustomizeColumns = ({columns, onColumnRename}) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<label>Customize Columns</label>
|
||||||
|
{columns.map(col => {
|
||||||
|
return (
|
||||||
|
<GraphOptionsCustomizableColumn
|
||||||
|
key={uuid.v4()}
|
||||||
|
originalColumnName={col.name}
|
||||||
|
newColumnName={col.newName}
|
||||||
|
onColumnRename={onColumnRename}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
const {arrayOf, func, shape, string} = PropTypes
|
||||||
|
|
||||||
|
GraphOptionsCustomizeColumns.propTypes = {
|
||||||
|
columns: arrayOf(
|
||||||
|
shape({
|
||||||
|
name: string,
|
||||||
|
newName: string,
|
||||||
|
})
|
||||||
|
),
|
||||||
|
onColumnRename: func,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GraphOptionsCustomizeColumns
|
|
@ -0,0 +1,28 @@
|
||||||
|
import React, {PropTypes} from 'react'
|
||||||
|
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-primary"
|
||||||
|
buttonSize="btn-xs"
|
||||||
|
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,43 @@
|
||||||
|
import React, {PropTypes} from 'react'
|
||||||
|
|
||||||
|
import {
|
||||||
|
SINGLE_STAT_BG,
|
||||||
|
SINGLE_STAT_TEXT,
|
||||||
|
} from 'src/dashboards/constants/gaugeColors'
|
||||||
|
|
||||||
|
// TODO: Needs major refactoring to make thresholds a much more general component to be shared between single stat, gauge, and table.
|
||||||
|
const GraphOptionsTextWrapping = ({singleStatType, onToggleTextWrapping}) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<label>Text Wrapping</label>
|
||||||
|
<ul className="nav nav-tablist nav-tablist-sm">
|
||||||
|
<li
|
||||||
|
className={`${singleStatType === SINGLE_STAT_BG ? 'active' : ''}`}
|
||||||
|
onClick={onToggleTextWrapping}
|
||||||
|
>
|
||||||
|
Truncate
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
className={`${singleStatType === SINGLE_STAT_TEXT ? 'active' : ''}`}
|
||||||
|
onClick={onToggleTextWrapping}
|
||||||
|
>
|
||||||
|
Wrap
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
className={`${singleStatType === SINGLE_STAT_BG ? 'active' : ''}`}
|
||||||
|
onClick={onToggleTextWrapping}
|
||||||
|
>
|
||||||
|
Single Line
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
const {func, string} = PropTypes
|
||||||
|
|
||||||
|
GraphOptionsTextWrapping.propTypes = {
|
||||||
|
singleStatType: string,
|
||||||
|
onToggleTextWrapping: func,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GraphOptionsTextWrapping
|
|
@ -0,0 +1,40 @@
|
||||||
|
import React, {PropTypes} from 'react'
|
||||||
|
|
||||||
|
import {
|
||||||
|
SINGLE_STAT_BG,
|
||||||
|
SINGLE_STAT_TEXT,
|
||||||
|
} from 'src/dashboards/constants/gaugeColors'
|
||||||
|
|
||||||
|
// TODO: Needs major refactoring to make thresholds a much more general component to be shared between single stat, gauge, and table.
|
||||||
|
const GraphOptionsThresholdColoring = ({
|
||||||
|
onToggleSingleStatType,
|
||||||
|
singleStatType,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<label>Threshold Coloring</label>
|
||||||
|
<ul className="nav nav-tablist nav-tablist-sm">
|
||||||
|
<li
|
||||||
|
className={`${singleStatType === SINGLE_STAT_BG ? 'active' : ''}`}
|
||||||
|
onClick={onToggleSingleStatType(SINGLE_STAT_BG)}
|
||||||
|
>
|
||||||
|
Background
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
className={`${singleStatType === SINGLE_STAT_TEXT ? 'active' : ''}`}
|
||||||
|
onClick={onToggleSingleStatType(SINGLE_STAT_TEXT)}
|
||||||
|
>
|
||||||
|
Text
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
const {func, string} = PropTypes
|
||||||
|
|
||||||
|
GraphOptionsThresholdColoring.propTypes = {
|
||||||
|
singleStatType: string,
|
||||||
|
onToggleSingleStatType: func,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GraphOptionsThresholdColoring
|
|
@ -0,0 +1,77 @@
|
||||||
|
import React, {PropTypes} from 'react'
|
||||||
|
import Threshold from 'src/dashboards/components/Threshold'
|
||||||
|
import ColorDropdown from 'shared/components/ColorDropdown'
|
||||||
|
|
||||||
|
import {
|
||||||
|
GAUGE_COLORS,
|
||||||
|
SINGLE_STAT_BASE,
|
||||||
|
} from 'src/dashboards/constants/gaugeColors'
|
||||||
|
|
||||||
|
// TODO: Needs major refactoring to make thresholds a much more general component to be shared between single stat, gauge, and table.
|
||||||
|
const GraphOptionsThresholds = ({
|
||||||
|
onAddThreshold,
|
||||||
|
disableAddThreshold,
|
||||||
|
sortedColors,
|
||||||
|
formatColor,
|
||||||
|
onChooseColor,
|
||||||
|
onValidateColorValue,
|
||||||
|
onUpdateColorValue,
|
||||||
|
onDeleteThreshold,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<label>Thresholds</label>
|
||||||
|
<button
|
||||||
|
className="btn btn-sm btn-primary gauge-controls--add-threshold"
|
||||||
|
onClick={onAddThreshold}
|
||||||
|
disabled={disableAddThreshold}
|
||||||
|
>
|
||||||
|
<span className="icon plus" /> Add Threshold
|
||||||
|
</button>
|
||||||
|
{sortedColors.map(
|
||||||
|
color =>
|
||||||
|
color.id === SINGLE_STAT_BASE
|
||||||
|
? <div className="gauge-controls--section" key={color.id}>
|
||||||
|
<div className="gauge-controls--label">Base Color</div>
|
||||||
|
<ColorDropdown
|
||||||
|
colors={GAUGE_COLORS}
|
||||||
|
selected={formatColor(color)}
|
||||||
|
onChoose={onChooseColor(color)}
|
||||||
|
stretchToFit={true}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
: <Threshold
|
||||||
|
visualizationType="single-stat"
|
||||||
|
threshold={color}
|
||||||
|
key={color.id}
|
||||||
|
onChooseColor={onChooseColor}
|
||||||
|
onValidateColorValue={onValidateColorValue}
|
||||||
|
onUpdateColorValue={onUpdateColorValue}
|
||||||
|
onDeleteThreshold={onDeleteThreshold}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
const {arrayOf, bool, func, shape, string, number} = PropTypes
|
||||||
|
|
||||||
|
GraphOptionsThresholds.propTypes = {
|
||||||
|
onAddThreshold: func,
|
||||||
|
disableAddThreshold: bool,
|
||||||
|
sortedColors: arrayOf(
|
||||||
|
shape({
|
||||||
|
hex: string,
|
||||||
|
id: string,
|
||||||
|
name: string,
|
||||||
|
type: string,
|
||||||
|
value: number,
|
||||||
|
})
|
||||||
|
),
|
||||||
|
formatColor: func,
|
||||||
|
onChooseColor: func,
|
||||||
|
onValidateColorValue: func,
|
||||||
|
onUpdateColorValue: func,
|
||||||
|
onDeleteThreshold: func,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GraphOptionsThresholds
|
|
@ -0,0 +1,28 @@
|
||||||
|
import React, {PropTypes} from 'react'
|
||||||
|
|
||||||
|
const VERTICAL = 'VERTICAL'
|
||||||
|
const HORIZONTAL = 'HORIZONTAL'
|
||||||
|
const GraphOptionsTimeAxis = ({TimeAxis, onToggleTimeAxis}) =>
|
||||||
|
<div className="form-group col-xs-6">
|
||||||
|
<label>Time Axis</label>
|
||||||
|
<ul className="nav nav-tablist nav-tablist-sm">
|
||||||
|
<li
|
||||||
|
className={`${TimeAxis === VERTICAL ? 'active' : ''}`}
|
||||||
|
onClick={onToggleTimeAxis}
|
||||||
|
>
|
||||||
|
Vertical
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
className={`${TimeAxis === HORIZONTAL ? 'active' : ''}`}
|
||||||
|
onClick={onToggleTimeAxis}
|
||||||
|
>
|
||||||
|
Horizontal
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
const {func, string} = PropTypes
|
||||||
|
|
||||||
|
GraphOptionsTimeAxis.propTypes = {TimeAxis: string, onToggleTimeAxis: func}
|
||||||
|
|
||||||
|
export default GraphOptionsTimeAxis
|
|
@ -0,0 +1,21 @@
|
||||||
|
import React, {PropTypes} from 'react'
|
||||||
|
|
||||||
|
const GraphOptionsTimeFormat = ({TimeFormat, onTimeFormatChange}) =>
|
||||||
|
<div>
|
||||||
|
<label>Time Format</label>
|
||||||
|
<input
|
||||||
|
className="form-control input-sm"
|
||||||
|
placeholder="mm/dd/yyyy HH:mm:ss.ss"
|
||||||
|
value={TimeFormat}
|
||||||
|
onChange={onTimeFormatChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
const {func, string} = PropTypes
|
||||||
|
|
||||||
|
GraphOptionsTimeFormat.propTypes = {
|
||||||
|
TimeFormat: string,
|
||||||
|
onTimeFormatChange: func,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GraphOptionsTimeFormat
|
|
@ -0,0 +1,166 @@
|
||||||
|
import React, {Component, PropTypes} from 'react'
|
||||||
|
import {connect} from 'react-redux'
|
||||||
|
import {bindActionCreators} from 'redux'
|
||||||
|
|
||||||
|
import _ from 'lodash'
|
||||||
|
|
||||||
|
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||||
|
import GraphOptionsTimeFormat from 'src/dashboards/components/GraphOptionsTimeFormat'
|
||||||
|
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 GraphOptionsThresholds from 'src/dashboards/components/GraphOptionsThresholds'
|
||||||
|
import GraphOptionsThresholdColoring from 'src/dashboards/components/GraphOptionsThresholdColoring'
|
||||||
|
|
||||||
|
import {MAX_THRESHOLDS} from 'src/dashboards/constants/gaugeColors'
|
||||||
|
|
||||||
|
import {
|
||||||
|
updateSingleStatType,
|
||||||
|
updateSingleStatColors,
|
||||||
|
updateAxes,
|
||||||
|
} from 'src/dashboards/actions/cellEditorOverlay'
|
||||||
|
|
||||||
|
const formatColor = color => {
|
||||||
|
const {hex, name} = color
|
||||||
|
return {hex, name}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TableOptions extends Component {
|
||||||
|
state = {TimeAxis: 'VERTICAL', TimeFormat: 'mm/dd/yyyy HH:mm:ss.ss'}
|
||||||
|
|
||||||
|
handleToggleSingleStatType = () => {}
|
||||||
|
|
||||||
|
handleAddThreshold = () => {}
|
||||||
|
|
||||||
|
handleDeleteThreshold = () => () => {}
|
||||||
|
|
||||||
|
handleChooseColor = () => () => {}
|
||||||
|
|
||||||
|
handleChooseSortBy = () => {}
|
||||||
|
|
||||||
|
handleTimeFormatChange = () => {}
|
||||||
|
|
||||||
|
handleToggleTimeAxis = () => {}
|
||||||
|
|
||||||
|
handleToggleTextWrapping = () => {}
|
||||||
|
|
||||||
|
handleColumnRename = () => {}
|
||||||
|
|
||||||
|
handleUpdateColorValue = () => {}
|
||||||
|
|
||||||
|
handleValidateColorValue = () => {}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
singleStatColors,
|
||||||
|
singleStatType,
|
||||||
|
// axes: {y: {prefix, suffix}},
|
||||||
|
} = this.props
|
||||||
|
|
||||||
|
const {TimeFormat, TimeAxis} = this.state
|
||||||
|
|
||||||
|
const disableAddThreshold = singleStatColors.length > MAX_THRESHOLDS
|
||||||
|
|
||||||
|
const sortedColors = _.sortBy(singleStatColors, color => color.value)
|
||||||
|
|
||||||
|
const columns = ['hey', 'yo', 'what'].map(col => ({
|
||||||
|
text: col,
|
||||||
|
name: col,
|
||||||
|
newName: '',
|
||||||
|
}))
|
||||||
|
const tableSortByOptions = ['hey', 'yo', 'what'].map(col => ({text: col}))
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FancyScrollbar
|
||||||
|
className="display-options--cell y-axis-controls"
|
||||||
|
autoHide={false}
|
||||||
|
>
|
||||||
|
<div className="display-options--cell-wrapper">
|
||||||
|
<h5 className="display-options--header">Table Controls</h5>
|
||||||
|
<div className="gauge-controls">
|
||||||
|
<GraphOptionsTimeFormat
|
||||||
|
TimeFormat={TimeFormat}
|
||||||
|
onTimeFormatChange={this.handleTimeFormatChange}
|
||||||
|
/>
|
||||||
|
<GraphOptionsTimeAxis
|
||||||
|
TimeAxis={TimeAxis}
|
||||||
|
onToggleTimeAxis={this.handleToggleTimeAxis}
|
||||||
|
/>
|
||||||
|
<GraphOptionsSortBy
|
||||||
|
sortByOptions={tableSortByOptions}
|
||||||
|
onChooseSortBy={this.handleChooseSortBy}
|
||||||
|
/>
|
||||||
|
<GraphOptionsTextWrapping
|
||||||
|
singleStatType={singleStatType}
|
||||||
|
onToggleTextWrapping={this.handleToggleTextWrapping}
|
||||||
|
/>
|
||||||
|
<GraphOptionsCustomizeColumns
|
||||||
|
columns={columns}
|
||||||
|
onColumnRename={this.handleColumnRename}
|
||||||
|
/>
|
||||||
|
<GraphOptionsThresholds
|
||||||
|
onAddThreshold={this.handleAddThreshold}
|
||||||
|
disableAddThreshold={disableAddThreshold}
|
||||||
|
sortedColors={sortedColors}
|
||||||
|
formatColor={formatColor}
|
||||||
|
onChooseColor={this.handleChooseColor}
|
||||||
|
onValidateColorValue={this.handleValidateColorValue}
|
||||||
|
onUpdateColorValue={this.handleUpdateColorValue}
|
||||||
|
onDeleteThreshold={this.handleDeleteThreshold}
|
||||||
|
/>
|
||||||
|
<GraphOptionsThresholdColoring
|
||||||
|
onToggleSingleStatType={this.handleToggleSingleStatType}
|
||||||
|
singleStatColors={singleStatType}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</FancyScrollbar>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const {arrayOf, func, number, shape, string} = PropTypes
|
||||||
|
|
||||||
|
TableOptions.defaultProps = {
|
||||||
|
colors: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
TableOptions.propTypes = {
|
||||||
|
singleStatType: string.isRequired,
|
||||||
|
singleStatColors: arrayOf(
|
||||||
|
shape({
|
||||||
|
type: string.isRequired,
|
||||||
|
hex: string.isRequired,
|
||||||
|
id: string.isRequired,
|
||||||
|
name: string.isRequired,
|
||||||
|
value: number.isRequired,
|
||||||
|
}).isRequired
|
||||||
|
),
|
||||||
|
handleUpdateSingleStatType: func.isRequired,
|
||||||
|
handleUpdateSingleStatColors: func.isRequired,
|
||||||
|
handleUpdateAxes: func.isRequired,
|
||||||
|
axes: shape({}).isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = ({
|
||||||
|
cellEditorOverlay: {singleStatType, singleStatColors, cell: {axes}},
|
||||||
|
}) => ({
|
||||||
|
singleStatType,
|
||||||
|
singleStatColors,
|
||||||
|
axes,
|
||||||
|
})
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => ({
|
||||||
|
handleUpdateSingleStatType: bindActionCreators(
|
||||||
|
updateSingleStatType,
|
||||||
|
dispatch
|
||||||
|
),
|
||||||
|
handleUpdateSingleStatColors: bindActionCreators(
|
||||||
|
updateSingleStatColors,
|
||||||
|
dispatch
|
||||||
|
),
|
||||||
|
handleUpdateAxes: bindActionCreators(updateAxes, dispatch),
|
||||||
|
})
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(TableOptions)
|
|
@ -86,6 +86,10 @@ export const GAUGE_COLORS = [
|
||||||
hex: '#ffffff',
|
hex: '#ffffff',
|
||||||
name: 'white',
|
name: 'white',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
hex: '#292933',
|
||||||
|
name: 'castle',
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
export const DEFAULT_GAUGE_COLORS = [
|
export const DEFAULT_GAUGE_COLORS = [
|
||||||
|
@ -115,6 +119,16 @@ export const DEFAULT_SINGLESTAT_COLORS = [
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
export const DEFAULT_TABLE_COLORS = [
|
||||||
|
{
|
||||||
|
type: SINGLE_STAT_BG,
|
||||||
|
hex: GAUGE_COLORS[18].hex,
|
||||||
|
id: SINGLE_STAT_BASE,
|
||||||
|
name: GAUGE_COLORS[18].name,
|
||||||
|
value: 0,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
export const validateSingleStatColors = (colors, type) => {
|
export const validateSingleStatColors = (colors, type) => {
|
||||||
if (!colors || colors.length === 0) {
|
if (!colors || colors.length === 0) {
|
||||||
return DEFAULT_SINGLESTAT_COLORS
|
return DEFAULT_SINGLESTAT_COLORS
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
export const GRAPH_TYPES = [
|
const GRAPH_SVGS = {
|
||||||
{
|
line: (
|
||||||
type: 'line',
|
|
||||||
menuOption: 'Line Graph',
|
|
||||||
graphic: (
|
|
||||||
<div className="viz-type-selector--graphic">
|
<div className="viz-type-selector--graphic">
|
||||||
<svg
|
<svg
|
||||||
width="100%"
|
width="100%"
|
||||||
|
@ -43,11 +40,7 @@ export const GRAPH_TYPES = [
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
'line-stacked': (
|
||||||
{
|
|
||||||
type: 'line-stacked',
|
|
||||||
menuOption: 'Stacked Graph',
|
|
||||||
graphic: (
|
|
||||||
<div className="viz-type-selector--graphic">
|
<div className="viz-type-selector--graphic">
|
||||||
<svg
|
<svg
|
||||||
width="100%"
|
width="100%"
|
||||||
|
@ -86,11 +79,7 @@ export const GRAPH_TYPES = [
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
'line-stepplot': (
|
||||||
{
|
|
||||||
type: 'line-stepplot',
|
|
||||||
menuOption: 'Step-Plot Graph',
|
|
||||||
graphic: (
|
|
||||||
<div className="viz-type-selector--graphic">
|
<div className="viz-type-selector--graphic">
|
||||||
<svg
|
<svg
|
||||||
width="100%"
|
width="100%"
|
||||||
|
@ -129,11 +118,7 @@ export const GRAPH_TYPES = [
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
'single-stat': (
|
||||||
{
|
|
||||||
type: 'single-stat',
|
|
||||||
menuOption: 'Single Stat',
|
|
||||||
graphic: (
|
|
||||||
<div className="viz-type-selector--graphic">
|
<div className="viz-type-selector--graphic">
|
||||||
<svg
|
<svg
|
||||||
width="100%"
|
width="100%"
|
||||||
|
@ -172,11 +157,7 @@ export const GRAPH_TYPES = [
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
'line-plus-single-stat': (
|
||||||
{
|
|
||||||
type: 'line-plus-single-stat',
|
|
||||||
menuOption: 'Line Graph + Single Stat',
|
|
||||||
graphic: (
|
|
||||||
<div className="viz-type-selector--graphic">
|
<div className="viz-type-selector--graphic">
|
||||||
<svg
|
<svg
|
||||||
width="100%"
|
width="100%"
|
||||||
|
@ -225,11 +206,7 @@ export const GRAPH_TYPES = [
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
bar: (
|
||||||
{
|
|
||||||
type: 'bar',
|
|
||||||
menuOption: 'Bar Graph',
|
|
||||||
graphic: (
|
|
||||||
<div className="viz-type-selector--graphic">
|
<div className="viz-type-selector--graphic">
|
||||||
<svg
|
<svg
|
||||||
width="100%"
|
width="100%"
|
||||||
|
@ -314,11 +291,7 @@ export const GRAPH_TYPES = [
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
gauge: (
|
||||||
{
|
|
||||||
type: 'gauge',
|
|
||||||
menuOption: 'Gauge',
|
|
||||||
graphic: (
|
|
||||||
<div className="viz-type-selector--graphic">
|
<div className="viz-type-selector--graphic">
|
||||||
<svg
|
<svg
|
||||||
width="100%"
|
width="100%"
|
||||||
|
@ -458,5 +431,82 @@ export const GRAPH_TYPES = [
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
|
table: (
|
||||||
|
<div className="viz-type-selector--graphic">
|
||||||
|
<svg
|
||||||
|
id="Table"
|
||||||
|
x="0px"
|
||||||
|
y="0px"
|
||||||
|
width="100%"
|
||||||
|
height="100%"
|
||||||
|
viewBox="0 0 550 550"
|
||||||
|
>
|
||||||
|
<g>
|
||||||
|
<path
|
||||||
|
className="viz-type-selector--graphic-line graphic-line-a"
|
||||||
|
d="M430.274,23.861H16.698C7.48,23.861,0,31.357,0,40.559v365.86c0,5.654,2.834,10.637,7.155,13.663v2.632h5.986 c1.146,0.252,2.332,0.401,3.557,0.401h413.576c1.214,0,2.396-0.149,3.545-0.401h0.821v-0.251 c7.082-1.938,12.336-8.362,12.336-16.044V40.564C446.977,31.357,439.478,23.861,430.274,23.861z M66,408.4H15.458 c-0.676-0.416-1.146-1.132-1.146-1.98v-43.35H66V408.4z M66,348.755H14.312v-47.01H66V348.755z M66,287.436H14.312v-49.632H66 V287.436z M66,223.491H14.312v-53.687H66V223.491z M66,155.49H14.312v-52.493H66V155.49z M186.497,408.4H80.318v-45.33h106.179 V408.4z M186.497,348.755H80.318v-47.01h106.179V348.755z M186.497,287.436H80.318v-49.632h106.179V287.436z M186.497,223.491 H80.318v-53.687h106.179V223.491z M186.497,155.49H80.318v-52.493h106.179V155.49z M186.497,88.68H80.318V38.17h106.179V88.68z M308.195,408.4H200.812v-45.33h107.383V408.4z M308.195,348.755H200.812v-47.01h107.383V348.755z M308.195,287.436H200.812 v-49.632h107.383V287.436z M308.195,223.491H200.812v-53.687h107.383V223.491z M308.195,155.49H200.812v-52.493h107.383V155.49z M308.195,88.68H200.812V38.17h107.383V88.68z M432.66,406.419c0,0.845-0.48,1.56-1.149,1.98h-109v-45.33H432.66V406.419z M432.66,348.755H322.511v-47.01H432.66V348.755z M432.66,287.436H322.511v-49.632H432.66V287.436z M432.66,223.491H322.511 v-53.687H432.66V223.491z M432.66,155.49H322.511v-52.493H432.66V155.49z M432.66,88.68H322.511V38.17h107.764 c1.312,0,2.386,1.073,2.386,2.389V88.68z M175.854,276.251H89.938V246.37h85.915V276.251z M297.261,277.378h-85.915v-29.883h85.915 V277.378z M421.661,276.721h-85.914v-29.883h85.914V276.721z"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
<g />
|
||||||
|
<g />
|
||||||
|
<g />
|
||||||
|
<g />
|
||||||
|
<g />
|
||||||
|
<g />
|
||||||
|
<g />
|
||||||
|
<g />
|
||||||
|
<g />
|
||||||
|
<g />
|
||||||
|
<g />
|
||||||
|
<g />
|
||||||
|
<g />
|
||||||
|
<g />
|
||||||
|
<g />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
export const GRAPH_TYPES = [
|
||||||
|
{
|
||||||
|
type: 'line',
|
||||||
|
menuOption: 'Line Graph',
|
||||||
|
graphic: GRAPH_SVGS.line,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'line-stacked',
|
||||||
|
menuOption: 'Stacked Graph',
|
||||||
|
graphic: GRAPH_SVGS['line-stacked'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'line-stepplot',
|
||||||
|
menuOption: 'Step-Plot Graph',
|
||||||
|
graphic: GRAPH_SVGS['line-stepplot'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'single-stat',
|
||||||
|
menuOption: 'Single Stat',
|
||||||
|
graphic: GRAPH_SVGS['single-stat'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'line-plus-single-stat',
|
||||||
|
menuOption: 'Line Graph + Single Stat',
|
||||||
|
graphic: GRAPH_SVGS['line-plus-single-stat'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'bar',
|
||||||
|
menuOption: 'Bar Graph',
|
||||||
|
graphic: GRAPH_SVGS.bar,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'gauge',
|
||||||
|
menuOption: 'Gauge',
|
||||||
|
graphic: GRAPH_SVGS.gauge,
|
||||||
|
},
|
||||||
|
// FEATURE FLAG for Table-Graph
|
||||||
|
// {
|
||||||
|
// type: 'table',
|
||||||
|
// menuOption: 'Table',
|
||||||
|
// graphic: GRAPH_SVGS.table,
|
||||||
|
// },
|
||||||
]
|
]
|
||||||
|
|
|
@ -6,10 +6,12 @@ import AutoRefresh from 'shared/components/AutoRefresh'
|
||||||
import LineGraph from 'shared/components/LineGraph'
|
import LineGraph from 'shared/components/LineGraph'
|
||||||
import SingleStat from 'shared/components/SingleStat'
|
import SingleStat from 'shared/components/SingleStat'
|
||||||
import GaugeChart from 'shared/components/GaugeChart'
|
import GaugeChart from 'shared/components/GaugeChart'
|
||||||
|
import TableGraph from 'shared/components/TableGraph'
|
||||||
|
|
||||||
const RefreshingLineGraph = AutoRefresh(LineGraph)
|
const RefreshingLineGraph = AutoRefresh(LineGraph)
|
||||||
const RefreshingSingleStat = AutoRefresh(SingleStat)
|
const RefreshingSingleStat = AutoRefresh(SingleStat)
|
||||||
const RefreshingGaugeChart = AutoRefresh(GaugeChart)
|
const RefreshingGaugeChart = AutoRefresh(GaugeChart)
|
||||||
|
const RefreshingTableGraph = AutoRefresh(TableGraph)
|
||||||
|
|
||||||
const RefreshingGraph = ({
|
const RefreshingGraph = ({
|
||||||
axes,
|
axes,
|
||||||
|
@ -79,6 +81,25 @@ const RefreshingGraph = ({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type === 'table') {
|
||||||
|
return (
|
||||||
|
<RefreshingTableGraph
|
||||||
|
colors={colors}
|
||||||
|
key={manualRefresh}
|
||||||
|
queries={queries}
|
||||||
|
templates={templates}
|
||||||
|
autoRefresh={autoRefresh}
|
||||||
|
cellHeight={cellHeight}
|
||||||
|
resizerTopHeight={resizerTopHeight}
|
||||||
|
resizeCoords={resizeCoords}
|
||||||
|
cellID={cellID}
|
||||||
|
// prefix={prefix}
|
||||||
|
// suffix={suffix}
|
||||||
|
inView={inView}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const displayOptions = {
|
const displayOptions = {
|
||||||
stepPlot: type === 'line-stepplot',
|
stepPlot: type === 'line-stepplot',
|
||||||
stackedGraph: type === 'line-stacked',
|
stackedGraph: type === 'line-stacked',
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
import React, {PropTypes, Component} from 'react'
|
||||||
|
import _ from 'lodash'
|
||||||
|
import {timeSeriesToTable} from 'src/utils/timeSeriesToDygraph'
|
||||||
|
import {MultiGrid} from 'react-virtualized'
|
||||||
|
|
||||||
|
class TableGraph extends Component {
|
||||||
|
componentWillMount() {
|
||||||
|
this._labels = []
|
||||||
|
this._data = [[]]
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUpdate(nextProps) {
|
||||||
|
// TODO: determine if in dataExplorer
|
||||||
|
const {labels, data} = timeSeriesToTable(nextProps.data)
|
||||||
|
this._labels = labels
|
||||||
|
this._data = data
|
||||||
|
}
|
||||||
|
|
||||||
|
cellRenderer = ({columnIndex, key, rowIndex, style}) => {
|
||||||
|
const data = this._data
|
||||||
|
return (
|
||||||
|
<div key={key} style={style}>
|
||||||
|
{data[rowIndex][columnIndex]}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const data = this._data
|
||||||
|
const columnCount = _.get(data, ['0', 'length'], 0)
|
||||||
|
const rowCount = data.length
|
||||||
|
const COLUMN_WIDTH = 300
|
||||||
|
const ROW_HEIGHT = 50
|
||||||
|
const tableWidth = this.gridContainer ? this.gridContainer.clientWidth : 0
|
||||||
|
const tableHeight = this.gridContainer ? this.gridContainer.clientHeight : 0
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="graph-container"
|
||||||
|
ref={gridContainer => (this.gridContainer = gridContainer)}
|
||||||
|
>
|
||||||
|
{data.length > 1 &&
|
||||||
|
<MultiGrid
|
||||||
|
fixedColumnCount={1}
|
||||||
|
fixedRowCount={1}
|
||||||
|
cellRenderer={this.cellRenderer}
|
||||||
|
columnCount={columnCount}
|
||||||
|
columnWidth={COLUMN_WIDTH}
|
||||||
|
height={tableHeight}
|
||||||
|
rowCount={rowCount}
|
||||||
|
rowHeight={ROW_HEIGHT}
|
||||||
|
width={tableWidth - 32}
|
||||||
|
/>}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const {arrayOf, number, shape} = PropTypes
|
||||||
|
|
||||||
|
TableGraph.propTypes = {
|
||||||
|
cellHeight: number,
|
||||||
|
data: arrayOf(shape()),
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TableGraph
|
|
@ -162,3 +162,11 @@ export default function timeSeriesToDygraph(raw = [], isInDataExplorer) {
|
||||||
dygraphSeries,
|
dygraphSeries,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const timeSeriesToTable = data => {
|
||||||
|
const {labels, timeSeries} = timeSeriesToDygraph(data, false)
|
||||||
|
const tableData = timeSeries.length
|
||||||
|
? timeSeries.map(row => row.map(cell => (cell ? cell.toString() : 'null')))
|
||||||
|
: [[]]
|
||||||
|
return {labels, data: [labels, ...tableData]}
|
||||||
|
}
|
||||||
|
|
16
ui/yarn.lock
16
ui/yarn.lock
|
@ -2523,6 +2523,10 @@ dom-css@^2.0.0:
|
||||||
prefix-style "2.0.1"
|
prefix-style "2.0.1"
|
||||||
to-camel-case "1.0.0"
|
to-camel-case "1.0.0"
|
||||||
|
|
||||||
|
"dom-helpers@^2.4.0 || ^3.0.0":
|
||||||
|
version "3.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.3.1.tgz#fc1a4e15ffdf60ddde03a480a9c0fece821dd4a6"
|
||||||
|
|
||||||
dom-serializer@0, dom-serializer@~0.1.0:
|
dom-serializer@0, dom-serializer@~0.1.0:
|
||||||
version "0.1.0"
|
version "0.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82"
|
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82"
|
||||||
|
@ -5384,7 +5388,7 @@ longest@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
|
resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
|
||||||
|
|
||||||
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1:
|
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.0, loose-envify@^1.3.1:
|
||||||
version "1.3.1"
|
version "1.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848"
|
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -7141,6 +7145,16 @@ react-tooltip@^3.2.1:
|
||||||
classnames "^2.2.5"
|
classnames "^2.2.5"
|
||||||
prop-types "^15.6.0"
|
prop-types "^15.6.0"
|
||||||
|
|
||||||
|
react-virtualized@^9.18.5:
|
||||||
|
version "9.18.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-virtualized/-/react-virtualized-9.18.5.tgz#42dd390ebaa7ea809bfcaf775d39872641679b89"
|
||||||
|
dependencies:
|
||||||
|
babel-runtime "^6.26.0"
|
||||||
|
classnames "^2.2.3"
|
||||||
|
dom-helpers "^2.4.0 || ^3.0.0"
|
||||||
|
loose-envify "^1.3.0"
|
||||||
|
prop-types "^15.6.0"
|
||||||
|
|
||||||
react@^15.0.2:
|
react@^15.0.2:
|
||||||
version "15.6.2"
|
version "15.6.2"
|
||||||
resolved "https://registry.yarnpkg.com/react/-/react-15.6.2.tgz#dba0434ab439cfe82f108f0f511663908179aa72"
|
resolved "https://registry.yarnpkg.com/react/-/react-15.6.2.tgz#dba0434ab439cfe82f108f0f511663908179aa72"
|
||||||
|
|
Loading…
Reference in New Issue