Merge pull request #5704 from influxdata/5528/filterFields
feat(ui): allow to filter fields in query builderpull/5712/head
commit
b86c173222
|
@ -22,6 +22,7 @@
|
|||
1. [#5688](https://github.com/influxdata/chronograf/pull/5688): Add UI variables to flux query execution.
|
||||
1. [#5697](https://github.com/influxdata/chronograf/pull/5697): Allow to define dashboard variables using flux.
|
||||
1. [#5700](https://github.com/influxdata/chronograf/pull/5700): Remove HipChat alerts.
|
||||
1. [#5704](https://github.com/influxdata/chronograf/pull/5704): Allow to filter fields in Query Builder UI.
|
||||
|
||||
### Other
|
||||
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
import React, {PureComponent, MouseEvent} from 'react'
|
||||
import classnames from 'classnames'
|
||||
import _ from 'lodash'
|
||||
|
||||
import FunctionSelector from 'src/shared/components/FunctionSelector'
|
||||
import {firstFieldName} from 'src/shared/reducers/helpers/fields'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
import {ApplyFuncsToFieldArgs, Field, FieldFunc, FuncArg} from 'src/types'
|
||||
|
||||
interface Props {
|
||||
fieldName: string
|
||||
fieldFuncs: FieldFunc[]
|
||||
isSelected: boolean
|
||||
onToggleField: (field: Field) => void
|
||||
|
@ -32,9 +31,14 @@ class FieldListItem extends PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
public render() {
|
||||
const {isKapacitorRule, isSelected, funcs, isDisabled} = this.props
|
||||
const {
|
||||
isKapacitorRule,
|
||||
isSelected,
|
||||
fieldName,
|
||||
funcs,
|
||||
isDisabled,
|
||||
} = this.props
|
||||
const {isOpen} = this.state
|
||||
const fieldName = this.getFieldName()
|
||||
|
||||
let fieldFuncsLabel
|
||||
const num = funcs.length
|
||||
|
@ -104,16 +108,14 @@ class FieldListItem extends PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
private handleToggleField = (): void => {
|
||||
const {onToggleField} = this.props
|
||||
const value = this.getFieldName()
|
||||
const {onToggleField, fieldName} = this.props
|
||||
|
||||
onToggleField({value, type: 'field'})
|
||||
onToggleField({value: fieldName, type: 'field'})
|
||||
this.close()
|
||||
}
|
||||
|
||||
private handleApplyFunctions = (selectedFuncs: string[]) => {
|
||||
const {onApplyFuncsToField} = this.props
|
||||
const fieldName = this.getFieldName()
|
||||
const {onApplyFuncsToField, fieldName} = this.props
|
||||
const field: Field = {value: fieldName, type: 'field'}
|
||||
|
||||
onApplyFuncsToField({
|
||||
|
@ -127,15 +129,6 @@ class FieldListItem extends PureComponent<Props, State> {
|
|||
value,
|
||||
type: 'func',
|
||||
})
|
||||
|
||||
private getFieldName = (): string => {
|
||||
const {fieldFuncs} = this.props
|
||||
const fieldFunc = _.head(fieldFuncs)
|
||||
|
||||
return _.get(fieldFunc, 'type') === 'field'
|
||||
? _.get(fieldFunc, 'value')
|
||||
: firstFieldName(_.get(fieldFunc, 'args'))
|
||||
}
|
||||
}
|
||||
|
||||
export default FieldListItem
|
||||
|
|
|
@ -22,8 +22,10 @@ import {
|
|||
numFunctions,
|
||||
getFieldsWithName,
|
||||
getFuncsByFieldName,
|
||||
getFieldName,
|
||||
} from 'src/shared/reducers/helpers/fields'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import QueryBuilderFilter from './QueryBuilderFilter'
|
||||
|
||||
interface GroupByOption extends GroupBy {
|
||||
menuOption: string
|
||||
|
@ -55,6 +57,7 @@ interface Props {
|
|||
}
|
||||
|
||||
interface State {
|
||||
filterText: string
|
||||
fields: Field[]
|
||||
}
|
||||
|
||||
|
@ -68,6 +71,7 @@ class FieldList extends PureComponent<Props, State> {
|
|||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
filterText: '',
|
||||
fields: [],
|
||||
}
|
||||
}
|
||||
|
@ -131,7 +135,13 @@ class FieldList extends PureComponent<Props, State> {
|
|||
onGroupByTime={this.handleGroupByTime}
|
||||
isDisabled={isDisabled}
|
||||
/>
|
||||
) : null}
|
||||
) : (
|
||||
<QueryBuilderFilter
|
||||
filterText={this.state.filterText}
|
||||
onEscape={this.handleEscapeFilter}
|
||||
onFilterText={this.handleFilterText}
|
||||
></QueryBuilderFilter>
|
||||
)}
|
||||
</div>
|
||||
{noDBorMeas ? (
|
||||
<div className="query-builder--list-empty">
|
||||
|
@ -141,41 +151,85 @@ class FieldList extends PureComponent<Props, State> {
|
|||
</div>
|
||||
) : (
|
||||
<div className="query-builder--list">
|
||||
<FancyScrollbar>
|
||||
{this.state.fields.map((fieldFunc, i) => {
|
||||
const selectedFields = getFieldsWithName(
|
||||
fieldFunc.value,
|
||||
fields
|
||||
)
|
||||
<div>
|
||||
<FancyScrollbar>
|
||||
{this.state.fields.map((fieldFunc, i) => {
|
||||
const selectedFields = getFieldsWithName(
|
||||
fieldFunc.value,
|
||||
fields
|
||||
)
|
||||
const fieldName = getFieldName(fieldFunc)
|
||||
if (
|
||||
this.state.filterText &&
|
||||
!selectedFields.length &&
|
||||
!fieldName
|
||||
.toLowerCase()
|
||||
.includes(this.state.filterText.toLowerCase())
|
||||
) {
|
||||
// do not render the item unless it is selected or matches filter
|
||||
return
|
||||
}
|
||||
|
||||
const funcs: FieldFunc[] = getFuncsByFieldName(
|
||||
fieldFunc.value,
|
||||
fields
|
||||
)
|
||||
const fieldFuncs = selectedFields.length
|
||||
? selectedFields
|
||||
: [fieldFunc]
|
||||
const funcs: FieldFunc[] = getFuncsByFieldName(
|
||||
fieldFunc.value,
|
||||
fields
|
||||
)
|
||||
const fieldFuncs = selectedFields.length
|
||||
? selectedFields
|
||||
: [fieldFunc]
|
||||
|
||||
return (
|
||||
<FieldListItem
|
||||
key={i}
|
||||
onToggleField={this.handleToggleField}
|
||||
onApplyFuncsToField={this.handleApplyFuncs}
|
||||
isSelected={!!selectedFields.length}
|
||||
fieldFuncs={fieldFuncs}
|
||||
funcs={functionNames(funcs)}
|
||||
isKapacitorRule={isKapacitorRule}
|
||||
isDisabled={isDisabled}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</FancyScrollbar>
|
||||
return (
|
||||
<FieldListItem
|
||||
key={i}
|
||||
onToggleField={this.handleToggleField}
|
||||
onApplyFuncsToField={this.handleApplyFuncs}
|
||||
isSelected={!!selectedFields.length}
|
||||
fieldName={fieldName}
|
||||
fieldFuncs={fieldFuncs}
|
||||
funcs={functionNames(funcs)}
|
||||
isKapacitorRule={isKapacitorRule}
|
||||
isDisabled={isDisabled}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</FancyScrollbar>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{hasAggregates ? (
|
||||
<div
|
||||
className="query-builder--heading"
|
||||
style={{backgroundColor: 'transparent'}}
|
||||
>
|
||||
<QueryBuilderFilter
|
||||
filterText={this.state.filterText}
|
||||
onEscape={this.handleEscapeFilter}
|
||||
onFilterText={this.handleFilterText}
|
||||
></QueryBuilderFilter>
|
||||
</div>
|
||||
) : undefined}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
private handleFilterText = (e: React.FormEvent<HTMLInputElement>) => {
|
||||
e.stopPropagation()
|
||||
const filterText = e.currentTarget.value
|
||||
this.setState({
|
||||
filterText,
|
||||
})
|
||||
}
|
||||
private handleEscapeFilter = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.key !== 'Escape') {
|
||||
return
|
||||
}
|
||||
|
||||
e.stopPropagation()
|
||||
this.setState({
|
||||
filterText: '',
|
||||
})
|
||||
}
|
||||
|
||||
private handleGroupByTime = (groupBy: GroupByOption): void => {
|
||||
this.props.onGroupByTime(groupBy.menuOption)
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import showMeasurementsParser from 'src/shared/parsing/showMeasurements'
|
|||
import {QueryConfig, Source, Tag} from 'src/types'
|
||||
|
||||
import FancyScrollbar from 'src/shared/components/FancyScrollbar'
|
||||
import MeasurementListFilter from 'src/shared/components/MeasurementListFilter'
|
||||
import QueryBuilderFilter from 'src/shared/components/QueryBuilderFilter'
|
||||
import MeasurementListItem from 'src/shared/components/MeasurementListItem'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
|
@ -136,7 +136,7 @@ class MeasurementList extends PureComponent<Props, State> {
|
|||
<div className="query-builder--heading">
|
||||
<span>Measurements & Tags</span>
|
||||
{database && (
|
||||
<MeasurementListFilter
|
||||
<QueryBuilderFilter
|
||||
onEscape={this.handleEscape}
|
||||
onFilterText={this.handleFilterText}
|
||||
filterText={this.state.filterText}
|
||||
|
|
|
@ -6,7 +6,7 @@ interface Props {
|
|||
onFilterText: (e: React.InputHTMLAttributes<HTMLInputElement>) => void
|
||||
}
|
||||
|
||||
const MeasurementListFilter: FunctionComponent<Props> = ({
|
||||
const QueryBuilderFilter: FunctionComponent<Props> = ({
|
||||
onEscape,
|
||||
onFilterText,
|
||||
filterText,
|
||||
|
@ -26,4 +26,4 @@ const MeasurementListFilter: FunctionComponent<Props> = ({
|
|||
</div>
|
||||
)
|
||||
|
||||
export default MeasurementListFilter
|
||||
export default QueryBuilderFilter
|
|
@ -87,3 +87,10 @@ export const removeField = (fieldName, fields) => {
|
|||
return [...acc, f]
|
||||
}, [])
|
||||
}
|
||||
|
||||
// Gets field name out of the field func supplied
|
||||
export function getFieldName(fieldFunc) {
|
||||
return _.get(fieldFunc, 'type') === 'field'
|
||||
? _.get(fieldFunc, 'value')
|
||||
: firstFieldName(_.get(fieldFunc, 'args'))
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import {shallow} from 'enzyme'
|
||||
import React from 'react'
|
||||
import MeasurementList from 'src/shared/components/MeasurementList'
|
||||
import MeasurementListFilter from 'src/shared/components/MeasurementListFilter'
|
||||
import QueryBuilderFilter from 'src/shared/components/QueryBuilderFilter'
|
||||
import MeasurementListItem from 'src/shared/components/MeasurementListItem'
|
||||
import {query, source} from 'test/resources'
|
||||
|
||||
|
@ -55,7 +55,7 @@ describe('Shared.Components.MeasurementList', () => {
|
|||
|
||||
it('renders <MeasurementListFilter/> to the page', () => {
|
||||
const {wrapper} = setup()
|
||||
const filter = wrapper.find(MeasurementListFilter)
|
||||
const filter = wrapper.find(QueryBuilderFilter)
|
||||
|
||||
expect(filter.exists()).toBe(true)
|
||||
})
|
||||
|
@ -69,7 +69,7 @@ describe('Shared.Components.MeasurementList', () => {
|
|||
const event = {target: {value: 'f'}, stopPropagation: () => {}}
|
||||
wrapper.setState({filtered: measurements, measurements})
|
||||
|
||||
const filter = wrapper.find(MeasurementListFilter)
|
||||
const filter = wrapper.find(QueryBuilderFilter)
|
||||
const input = filter.dive().find('input')
|
||||
input.simulate('change', event)
|
||||
wrapper.update()
|
||||
|
|
Loading…
Reference in New Issue