Merge pull request #3155 from influxdata/table/fieldnames-from-query-config

Table/Get FieldNames from query config
pull/10616/head
Iris Scholten 2018-04-09 15:59:25 -07:00 committed by GitHub
commit 5b87bea961
11 changed files with 102 additions and 311 deletions

View File

@ -49,7 +49,6 @@ class CellEditorOverlay extends Component {
activeQueryIndex: 0,
isDisplayOptionsTabActive: false,
staticLegend: IS_STATIC_LEGEND(legend),
dataLabels: [],
}
}
@ -253,10 +252,6 @@ class CellEditorOverlay extends Component {
this.overlayRef.focus()
}
setDataLabels = dataLabels => {
this.setState({dataLabels})
}
render() {
const {
onCancel,
@ -271,7 +266,6 @@ class CellEditorOverlay extends Component {
isDisplayOptionsTabActive,
queriesWorkingDraft,
staticLegend,
dataLabels,
} = this.state
const queryActions = {
@ -304,7 +298,6 @@ class CellEditorOverlay extends Component {
queryConfigs={queriesWorkingDraft}
editQueryStatus={editQueryStatus}
staticLegend={staticLegend}
setDataLabels={this.setDataLabels}
/>
<CEOBottom>
<OverlayControls
@ -324,7 +317,6 @@ class CellEditorOverlay extends Component {
onToggleStaticLegend={this.handleToggleStaticLegend}
staticLegend={staticLegend}
onResetFocus={this.handleResetFocus}
dataLabels={dataLabels}
/>
) : (
<QueryMaker

View File

@ -42,7 +42,7 @@ class DisplayOptions extends Component {
staticLegend,
onToggleStaticLegend,
onResetFocus,
dataLabels,
queryConfigs,
} = this.props
switch (type) {
case 'gauge':
@ -51,7 +51,10 @@ class DisplayOptions extends Component {
return <SingleStatOptions onResetFocus={onResetFocus} />
case 'table':
return (
<TableOptions onResetFocus={onResetFocus} dataLabels={dataLabels} />
<TableOptions
onResetFocus={onResetFocus}
queryConfigs={queryConfigs}
/>
)
default:
return (
@ -90,7 +93,6 @@ DisplayOptions.propTypes = {
onToggleStaticLegend: func.isRequired,
staticLegend: bool,
onResetFocus: func.isRequired,
dataLabels: arrayOf(string),
}
const mapStateToProps = ({cellEditorOverlay: {cell, cell: {axes}}}) => ({

View File

@ -23,16 +23,6 @@ class GraphOptionsCustomizableField extends PureComponent<Props, {}> {
this.handleToggleVisible = this.handleToggleVisible.bind(this)
}
public handleFieldRename(rename: string) {
const {onFieldUpdate, internalName, visible} = this.props
onFieldUpdate({internalName, displayName: rename, visible})
}
public handleToggleVisible() {
const {onFieldUpdate, internalName, displayName, visible} = this.props
onFieldUpdate({internalName, displayName, visible: !visible})
}
public render() {
const {internalName, displayName, visible} = this.props
@ -65,6 +55,16 @@ class GraphOptionsCustomizableField extends PureComponent<Props, {}> {
</div>
)
}
private handleFieldRename(rename: string) {
const {onFieldUpdate, internalName, visible} = this.props
onFieldUpdate({internalName, displayName: rename, visible})
}
private handleToggleVisible() {
const {onFieldUpdate, internalName, displayName, visible} = this.props
onFieldUpdate({internalName, displayName, visible: !visible})
}
}
export default GraphOptionsCustomizableField

View File

@ -16,6 +16,7 @@ import ThresholdsListTypeToggle from 'src/shared/components/ThresholdsListTypeTo
import {updateTableOptions} from 'src/dashboards/actions/cellEditorOverlay'
import {TIME_FIELD_DEFAULT} from 'src/shared/constants/tableGraph'
import {QueryConfig} from 'src/types/query'
interface Option {
text: string
@ -37,10 +38,10 @@ interface Options {
}
interface Props {
queryConfigs: QueryConfig[]
handleUpdateTableOptions: (options: Options) => void
tableOptions: Options
onResetFocus: () => void
dataLabels: string[]
}
export class TableOptions extends PureComponent<Props, {}> {
@ -48,79 +49,6 @@ export class TableOptions extends PureComponent<Props, {}> {
super(props)
}
get fieldNames() {
const {tableOptions: {fieldNames}} = this.props
return fieldNames || []
}
get timeField() {
return (
this.fieldNames.find(f => f.internalName === 'time') || TIME_FIELD_DEFAULT
)
}
get computedFieldNames() {
const {dataLabels} = this.props
return _.isEmpty(dataLabels)
? [this.timeField]
: dataLabels.map(label => {
const existing = this.fieldNames.find(f => f.internalName === label)
return (
existing || {internalName: label, displayName: '', visible: true}
)
})
}
public handleChooseSortBy = (option: Option) => {
const {tableOptions, handleUpdateTableOptions} = this.props
const sortBy = {
displayName: option.text === option.key ? '' : option.text,
internalName: option.key,
visible: true,
}
handleUpdateTableOptions({...tableOptions, sortBy})
}
public handleTimeFormatChange = timeFormat => {
const {tableOptions, handleUpdateTableOptions} = this.props
handleUpdateTableOptions({...tableOptions, timeFormat})
}
public handleToggleVerticalTimeAxis = verticalTimeAxis => () => {
const {tableOptions, handleUpdateTableOptions} = this.props
handleUpdateTableOptions({...tableOptions, verticalTimeAxis})
}
public handleToggleFixFirstColumn = () => {
const {handleUpdateTableOptions, tableOptions} = this.props
const fixFirstColumn = !tableOptions.fixFirstColumn
handleUpdateTableOptions({...tableOptions, fixFirstColumn})
}
public handleFieldUpdate = field => {
const {handleUpdateTableOptions, tableOptions, dataLabels} = this.props
const {sortBy, fieldNames} = tableOptions
const fields =
fieldNames.length >= dataLabels.length
? fieldNames
: this.computedFieldNames
const updatedFields = fields.map(
f => (f.internalName === field.internalName ? field : f)
)
const updatedSortBy =
sortBy.internalName === field.internalName
? {...sortBy, displayName: field.displayName}
: sortBy
handleUpdateTableOptions({
...tableOptions,
fieldNames: updatedFields,
sortBy: updatedSortBy,
})
}
public componentWillMount() {
const {handleUpdateTableOptions, tableOptions} = this.props
handleUpdateTableOptions({
@ -130,14 +58,13 @@ export class TableOptions extends PureComponent<Props, {}> {
}
public shouldComponentUpdate(nextProps) {
const {tableOptions, dataLabels} = this.props
const {tableOptions} = this.props
const tableOptionsDifferent = !_.isEqual(
tableOptions,
nextProps.tableOptions
)
const dataLabelsDifferent = !_.isEqual(dataLabels, nextProps.dataLabels)
return tableOptionsDifferent || dataLabelsDifferent
return tableOptionsDifferent
}
public render() {
@ -147,13 +74,11 @@ export class TableOptions extends PureComponent<Props, {}> {
tableOptions,
} = this.props
const tableSortByOptions = this.computedFieldNames.map(field => ({
const tableSortByOptions = this.fieldNames.map(field => ({
key: field.internalName,
text: field.displayName || field.internalName,
}))
const fields = fieldNames.length > 1 ? fieldNames : this.computedFieldNames
return (
<FancyScrollbar
className="display-options--cell y-axis-controls"
@ -181,7 +106,7 @@ export class TableOptions extends PureComponent<Props, {}> {
/>
</div>
<GraphOptionsCustomizeFields
fields={fields}
fields={fieldNames}
onFieldUpdate={this.handleFieldUpdate}
/>
<ThresholdsList showListHeading={true} onResetFocus={onResetFocus} />
@ -192,6 +117,79 @@ export class TableOptions extends PureComponent<Props, {}> {
</FancyScrollbar>
)
}
private get fieldNames() {
const {tableOptions: {fieldNames}} = this.props
return fieldNames || []
}
private get timeField() {
return (
this.fieldNames.find(f => f.internalName === 'time') || TIME_FIELD_DEFAULT
)
}
private get computedFieldNames() {
const {queryConfigs} = this.props
const queryFields = _.flatten(
queryConfigs.map(({measurement, fields}) => {
return fields.map(({alias}) => {
const internalName = `${measurement}.${alias}`
const existing = this.fieldNames.find(
c => c.internalName === internalName
)
return existing || {internalName, displayName: '', visible: true}
})
})
)
return [this.timeField, ...queryFields]
}
private handleChooseSortBy = (option: Option) => {
const {tableOptions, handleUpdateTableOptions} = this.props
const sortBy = {
displayName: option.text === option.key ? '' : option.text,
internalName: option.key,
visible: true,
}
handleUpdateTableOptions({...tableOptions, sortBy})
}
private handleTimeFormatChange = timeFormat => {
const {tableOptions, handleUpdateTableOptions} = this.props
handleUpdateTableOptions({...tableOptions, timeFormat})
}
private handleToggleVerticalTimeAxis = verticalTimeAxis => () => {
const {tableOptions, handleUpdateTableOptions} = this.props
handleUpdateTableOptions({...tableOptions, verticalTimeAxis})
}
private handleToggleFixFirstColumn = () => {
const {handleUpdateTableOptions, tableOptions} = this.props
const fixFirstColumn = !tableOptions.fixFirstColumn
handleUpdateTableOptions({...tableOptions, fixFirstColumn})
}
private handleFieldUpdate = field => {
const {handleUpdateTableOptions, tableOptions} = this.props
const {sortBy, fieldNames} = tableOptions
const updatedFields = fieldNames.map(
f => (f.internalName === field.internalName ? field : f)
)
const updatedSortBy =
sortBy.internalName === field.internalName
? {...sortBy, displayName: field.displayName}
: sortBy
handleUpdateTableOptions({
...tableOptions,
fieldNames: updatedFields,
sortBy: updatedSortBy,
})
}
}
const mapStateToProps = ({cellEditorOverlay: {cell: {tableOptions}}}) => ({

View File

@ -24,7 +24,6 @@ const DashVisualization = (
staticLegend,
thresholdsListColors,
tableOptions,
setDataLabels,
},
{source: {links: {proxy}}}
) => {
@ -50,7 +49,6 @@ const DashVisualization = (
editQueryStatus={editQueryStatus}
resizerTopHeight={resizerTopHeight}
staticLegend={staticLegend}
setDataLabels={setDataLabels}
/>
</div>
</div>
@ -80,7 +78,6 @@ DashVisualization.propTypes = {
gaugeColors: colorsNumberSchema,
lineColors: colorsStringSchema,
staticLegend: bool,
setDataLabels: func,
}
DashVisualization.contextTypes = {

View File

@ -25,7 +25,6 @@ const RefreshingGraph = ({
cellID,
queries,
tableOptions,
setDataLabels,
templates,
timeRange,
cellHeight,
@ -101,7 +100,6 @@ const RefreshingGraph = ({
hoverTime={hoverTime}
onSetHoverTime={onSetHoverTime}
inView={inView}
setDataLabels={setDataLabels}
/>
)
}
@ -160,7 +158,6 @@ RefreshingGraph.propTypes = {
cellID: string,
inView: bool,
tableOptions: shape({}),
setDataLabels: func,
}
RefreshingGraph.defaultProps = {

View File

@ -40,7 +40,6 @@ class TableGraph extends Component {
data: [[]],
processedData: [[]],
sortedTimeVals: [],
labels: [],
hoveredColumnIndex: NULL_ARRAY_INDEX,
hoveredRowIndex: NULL_ARRAY_INDEX,
sortField,
@ -51,7 +50,7 @@ class TableGraph extends Component {
}
componentWillReceiveProps(nextProps) {
const {labels, data} = timeSeriesToTableGraph(nextProps.data)
const {data} = timeSeriesToTableGraph(nextProps.data)
if (_.isEmpty(data[0])) {
return
}
@ -64,13 +63,8 @@ class TableGraph extends Component {
verticalTimeAxis,
timeFormat,
},
setDataLabels,
} = nextProps
if (setDataLabels) {
setDataLabels(labels)
}
let direction, sortFieldName
if (
_.get(this.props, ['tableOptions', 'sortBy', 'internalName'], '') ===
@ -99,7 +93,6 @@ class TableGraph extends Component {
this.setState({
data,
labels,
processedData,
sortedTimeVals,
sortField: sortFieldName,
@ -194,7 +187,7 @@ class TableGraph extends Component {
})
}
calculateColumnWidth = __ => column => {
calculateColumnWidth = columnSizerWidth => column => {
const {index} = column
const {tableOptions: {fixFirstColumn}} = this.props
const {processedData, columnWidths, totalColumnWidths} = this.state
@ -205,8 +198,12 @@ class TableGraph extends Component {
const tableWidth = _.get(this, ['gridContainer', 'clientWidth'], 0)
if (tableWidth > totalColumnWidths) {
if (columnCount === 1) {
return columnSizerWidth
}
const difference = tableWidth - totalColumnWidths
const distributeOver = fixFirstColumn ? columnCount - 1 : columnCount
const distributeOver =
fixFirstColumn && columnCount > 1 ? columnCount - 1 : columnCount
const increment = difference / distributeOver
adjustedColumnSizerWidth =
fixFirstColumn && index === 0
@ -412,7 +409,6 @@ TableGraph.propTypes = {
hoverTime: string,
onSetHoverTime: func,
colors: colorsStringSchema,
setDataLabels: func,
}
export default TableGraph

View File

@ -180,7 +180,6 @@ export const timeSeriesToTableGraph = raw => {
const tableData = map(sortedTimeSeries, ({time, values}) => [time, ...values])
const data = tableData.length ? [labels, ...tableData] : [[]]
return {
labels,
data,
}
}

View File

@ -72,47 +72,4 @@ describe('Dashboards.Components.GraphOptionsCustomizableField', () => {
})
})
})
describe('instance methods', () => {
describe('#handleFieldUpdate', () => {
it('calls onFieldUpdate once with internalName, new name, and visible', () => {
const onFieldUpdate = jest.fn()
const internalName = 'test'
const {instance, props: {visible}} = setup({
internalName,
onFieldUpdate,
})
const rename = 'TEST'
instance.handleFieldRename(rename)
expect(onFieldUpdate).toHaveBeenCalledTimes(1)
expect(onFieldUpdate).toHaveBeenCalledWith({
displayName: rename,
internalName,
visible,
})
})
})
describe('#handleToggleVisible', () => {
it('calls onFieldUpdate once with !visible, internalName, and displayName', () => {
const onFieldUpdate = jest.fn()
const visible = true
const {instance, props: {internalName, displayName}} = setup({
onFieldUpdate,
visible,
})
instance.handleToggleVisible()
expect(onFieldUpdate).toHaveBeenCalledTimes(1)
expect(onFieldUpdate).toHaveBeenCalledWith({
displayName,
internalName,
visible: !visible,
})
})
})
})
})

View File

@ -9,12 +9,11 @@ import {TableOptions} from 'src/dashboards/components/TableOptions'
import FancyScrollbar from 'src/shared/components/FancyScrollbar'
import ThresholdsList from 'src/shared/components/ThresholdsList'
import ThresholdsListTypeToggle from 'src/shared/components/ThresholdsListTypeToggle'
import {TIME_FIELD_DEFAULT} from 'src/shared/constants/tableGraph'
const defaultProps = {
dataLabels: [],
handleUpdateTableOptions: () => {},
onResetFocus: () => {},
queryConfigs: [],
tableOptions: {
columnNames: [],
fieldNames: [],
@ -39,100 +38,6 @@ const setup = (override = {}) => {
}
describe('Dashboards.Components.TableOptions', () => {
describe('getters', () => {
describe('fieldNames', () => {
describe('if fieldNames are passed in tableOptions as props', () => {
it('returns fieldNames', () => {
const fieldNames = [
{internalName: 'time', displayName: 'TIME', visible: true},
{internalName: 'foo', displayName: 'BAR', visible: false},
]
const {instance} = setup({tableOptions: {fieldNames}})
expect(instance.fieldNames).toBe(fieldNames)
})
})
describe('if fieldNames are not passed in tableOptions as props', () => {
it('returns empty array', () => {
const {instance} = setup()
expect(instance.fieldNames).toEqual([])
})
})
})
describe('timeField', () => {
describe('if time field in fieldNames', () => {
it('returns time field', () => {
const timeField = {
internalName: 'time',
displayName: 'TIME',
visible: true,
}
const fieldNames = [
timeField,
{internalName: 'foo', displayName: 'BAR', visible: false},
]
const {instance} = setup({tableOptions: {fieldNames}})
expect(instance.timeField).toBe(timeField)
})
})
describe('if time field not in fieldNames', () => {
it('returns default time field', () => {
const fieldNames = [
{internalName: 'foo', displayName: 'BAR', visible: false},
]
const {instance} = setup({tableOptions: {fieldNames}})
expect(instance.timeField).toBe(TIME_FIELD_DEFAULT)
})
})
})
describe('computedFieldNames', () => {
describe('if dataLabels are not passed in', () => {
it('returns an array of the time column', () => {
const {instance} = setup()
expect(instance.computedFieldNames).toEqual([TIME_FIELD_DEFAULT])
})
})
describe('if dataLabels are passed in', () => {
describe('if dataLabel has a matching fieldName', () => {
it('returns array with the matching fieldName', () => {
const fieldNames = [
{internalName: 'foo', displayName: 'bar', visible: true},
]
const dataLabels = ['foo']
const {instance} = setup({tableOptions: {fieldNames}, dataLabels})
expect(instance.computedFieldNames).toEqual(fieldNames)
})
})
describe('if dataLabel does not have a matching fieldName', () => {
it('returns array with a new fieldName created for it', () => {
const fieldNames = [
{internalName: 'time', displayName: 'bar', visible: true},
]
const unmatchedLabel = 'foo'
const dataLabels = ['time', unmatchedLabel]
const {instance} = setup({tableOptions: {fieldNames}, dataLabels})
expect(instance.computedFieldNames).toEqual([
...fieldNames,
{internalName: unmatchedLabel, displayName: '', visible: true},
])
})
})
})
})
})
describe('rendering', () => {
it('should render all components', () => {
const {wrapper} = setup()

View File

@ -351,58 +351,6 @@ describe('timeSeriesToTableGraph', () => {
expect(actual.data).toEqual(expected)
})
it('returns labels starting with time and then alphabetized', () => {
const influxResponse = [
{
response: {
results: [
{
series: [
{
name: 'mb',
columns: ['time', 'f1'],
values: [[1000, 1], [2000, 2]],
},
],
},
{
series: [
{
name: 'ma',
columns: ['time', 'f1'],
values: [[1000, 1], [2000, 2]],
},
],
},
{
series: [
{
name: 'mc',
columns: ['time', 'f2'],
values: [[2000, 3], [4000, 4]],
},
],
},
{
series: [
{
name: 'mc',
columns: ['time', 'f1'],
values: [[2000, 3], [4000, 4]],
},
],
},
],
},
},
]
const actual = timeSeriesToTableGraph(influxResponse)
const expected = ['time', 'ma.f1', 'mb.f1', 'mc.f1', 'mc.f2']
expect(actual.labels).toEqual(expected)
})
it('parses raw data into a table-readable format with the first row being labels', () => {
const influxResponse = [
{