chore(ui): add filter and group concepts to tags in redux

pull/16050/head
Bucky Schwarz 2019-11-20 17:04:43 -08:00 committed by Bucky Schwarz
parent 380b4113cc
commit a90899ebf2
6 changed files with 182 additions and 201 deletions

View File

@ -7862,6 +7862,11 @@ components:
type: array type: array
items: items:
type: string type: string
aggregateFunctionType:
$ref: '#/components/schemas/BuilderAggregateFunctionType'
BuilderAggregateFunctionType:
type: string
enum: ['filter', 'group']
BuilderFunctionsType: BuilderFunctionsType:
type: object type: object
properties: properties:

View File

@ -23,7 +23,7 @@ module.exports = {
'ts-jest': { 'ts-jest': {
tsConfig: 'tsconfig.test.json', tsConfig: 'tsconfig.test.json',
diagnostics: { diagnostics: {
ignoreCodes: [6133] // ignore `'foo' is declared but its value is never read.` ignoreCodes: [6133, 6192] // ignore unused variable errors
}, },
}, },
}, },

View File

@ -10,220 +10,113 @@ import {
// Types // Types
import {Dispatch} from 'redux-thunk' import {Dispatch} from 'redux-thunk'
import {GetState} from 'src/types' import {GetState, RemoteDataState} from 'src/types'
import {RemoteDataState} from 'src/types' import {BuilderAggregateFunctionType} from 'src/client/generatedRoutes'
import {BuilderFunctionsType} from '@influxdata/influx' import {BuilderFunctionsType} from '@influxdata/influx'
export type Action = export type Action =
| SetBuilderBucketSelectionAction | ReturnType<typeof setBuilderAggregateFunctionType>
| SetBuilderBucketsAction | ReturnType<typeof setBuilderBucket>
| SetBuilderBucketsStatusAction | ReturnType<typeof setBuilderBuckets>
| SetBuilderTagKeysAction | ReturnType<typeof setBuilderBucketsStatus>
| SetBuilderTagKeysStatusAction | ReturnType<typeof setBuilderTagKeys>
| SetBuilderTagValuesAction | ReturnType<typeof setBuilderTagKeysStatus>
| SetBuilderTagValuesStatusAction | ReturnType<typeof setBuilderTagValues>
| SetBuilderTagKeySelectionAction | ReturnType<typeof setBuilderTagValuesStatus>
| SetBuilderTagValuesSelectionAction | ReturnType<typeof setBuilderTagKeySelection>
| AddTagSelectorAction | ReturnType<typeof setBuilderTagValuesSelection>
| RemoveTagSelectorAction | ReturnType<typeof addTagSelectorSync>
| SetFunctionsAction | ReturnType<typeof removeTagSelectorSync>
| SelectAggregateWindowAction | ReturnType<typeof setFunctions>
| SetValuesSearchTermAction | ReturnType<typeof selectAggregateWindow>
| SetKeysSearchTermAction | ReturnType<typeof setValuesSearchTerm>
| SetBuilderTagsStatusAction | ReturnType<typeof setKeysSearchTerm>
| ReturnType<typeof setBuilderTagsStatus>
interface SetBuilderBucketsStatusAction { export const setBuilderAggregateFunctionType = (
type: 'SET_BUILDER_BUCKETS_STATUS' builderAggregateFunctionType: BuilderAggregateFunctionType,
payload: {bucketsStatus: RemoteDataState} index: number
} ) => ({
type: 'SET_BUILDER_AGGREGATE_FUNCTION_TYPE' as 'SET_BUILDER_AGGREGATE_FUNCTION_TYPE',
payload: {builderAggregateFunctionType, index},
})
const setBuilderBucketsStatus = ( const setBuilderBucketsStatus = (bucketsStatus: RemoteDataState) => ({
bucketsStatus: RemoteDataState type: 'SET_BUILDER_BUCKETS_STATUS' as 'SET_BUILDER_BUCKETS_STATUS',
): SetBuilderBucketsStatusAction => ({
type: 'SET_BUILDER_BUCKETS_STATUS',
payload: {bucketsStatus}, payload: {bucketsStatus},
}) })
interface SetBuilderBucketsAction { export const setBuilderBuckets = (buckets: string[]) => ({
type: 'SET_BUILDER_BUCKETS' type: 'SET_BUILDER_BUCKETS' as 'SET_BUILDER_BUCKETS',
payload: {buckets: string[]}
}
export const setBuilderBuckets = (
buckets: string[]
): SetBuilderBucketsAction => ({
type: 'SET_BUILDER_BUCKETS',
payload: {buckets}, payload: {buckets},
}) })
interface SetBuilderBucketSelectionAction { const setBuilderBucket = (bucket: string, resetSelections: boolean) => ({
type: 'SET_BUILDER_BUCKET_SELECTION' type: 'SET_BUILDER_BUCKET_SELECTION' as 'SET_BUILDER_BUCKET_SELECTION',
payload: {bucket: string; resetSelections: boolean}
}
const setBuilderBucket = (
bucket: string,
resetSelections: boolean
): SetBuilderBucketSelectionAction => ({
type: 'SET_BUILDER_BUCKET_SELECTION',
payload: {bucket, resetSelections}, payload: {bucket, resetSelections},
}) })
interface SetBuilderTagsStatusAction { export const setBuilderTagsStatus = (status: RemoteDataState) => ({
type: 'SET_BUILDER_TAGS_STATUS' type: 'SET_BUILDER_TAGS_STATUS' as 'SET_BUILDER_TAGS_STATUS',
payload: {status: RemoteDataState}
}
export const setBuilderTagsStatus = (
status: RemoteDataState
): SetBuilderTagsStatusAction => ({
type: 'SET_BUILDER_TAGS_STATUS',
payload: {status}, payload: {status},
}) })
interface SetBuilderTagKeysAction { const setBuilderTagKeys = (index: number, keys: string[]) => ({
type: 'SET_BUILDER_TAG_KEYS' type: 'SET_BUILDER_TAG_KEYS' as 'SET_BUILDER_TAG_KEYS',
payload: {index: number; keys: string[]}
}
const setBuilderTagKeys = (
index: number,
keys: string[]
): SetBuilderTagKeysAction => ({
type: 'SET_BUILDER_TAG_KEYS',
payload: {index, keys}, payload: {index, keys},
}) })
interface SetBuilderTagKeysStatusAction { const setBuilderTagKeysStatus = (index: number, status: RemoteDataState) => ({
type: 'SET_BUILDER_TAG_KEYS_STATUS' type: 'SET_BUILDER_TAG_KEYS_STATUS' as 'SET_BUILDER_TAG_KEYS_STATUS',
payload: {index: number; status: RemoteDataState}
}
const setBuilderTagKeysStatus = (
index: number,
status: RemoteDataState
): SetBuilderTagKeysStatusAction => ({
type: 'SET_BUILDER_TAG_KEYS_STATUS',
payload: {index, status}, payload: {index, status},
}) })
interface SetBuilderTagValuesAction { const setBuilderTagValues = (index: number, values: string[]) => ({
type: 'SET_BUILDER_TAG_VALUES' type: 'SET_BUILDER_TAG_VALUES' as 'SET_BUILDER_TAG_VALUES',
payload: {index: number; values: string[]}
}
const setBuilderTagValues = (
index: number,
values: string[]
): SetBuilderTagValuesAction => ({
type: 'SET_BUILDER_TAG_VALUES',
payload: {index, values}, payload: {index, values},
}) })
interface SetBuilderTagValuesStatusAction { const setBuilderTagValuesStatus = (index: number, status: RemoteDataState) => ({
type: 'SET_BUILDER_TAG_VALUES_STATUS' type: 'SET_BUILDER_TAG_VALUES_STATUS' as 'SET_BUILDER_TAG_VALUES_STATUS',
payload: {index: number; status: RemoteDataState}
}
const setBuilderTagValuesStatus = (
index: number,
status: RemoteDataState
): SetBuilderTagValuesStatusAction => ({
type: 'SET_BUILDER_TAG_VALUES_STATUS',
payload: {index, status}, payload: {index, status},
}) })
interface SetBuilderTagKeySelectionAction { const setBuilderTagKeySelection = (index: number, key: string) => ({
type: 'SET_BUILDER_TAG_KEY_SELECTION' type: 'SET_BUILDER_TAG_KEY_SELECTION' as 'SET_BUILDER_TAG_KEY_SELECTION',
payload: {index: number; key: string}
}
const setBuilderTagKeySelection = (
index: number,
key: string
): SetBuilderTagKeySelectionAction => ({
type: 'SET_BUILDER_TAG_KEY_SELECTION',
payload: {index, key}, payload: {index, key},
}) })
interface SetBuilderTagValuesSelectionAction { const setBuilderTagValuesSelection = (index: number, values: string[]) => ({
type: 'SET_BUILDER_TAG_VALUES_SELECTION' type: 'SET_BUILDER_TAG_VALUES_SELECTION' as 'SET_BUILDER_TAG_VALUES_SELECTION',
payload: {index: number; values: string[]}
}
const setBuilderTagValuesSelection = (
index: number,
values: string[]
): SetBuilderTagValuesSelectionAction => ({
type: 'SET_BUILDER_TAG_VALUES_SELECTION',
payload: {index, values}, payload: {index, values},
}) })
interface AddTagSelectorAction { const addTagSelectorSync = () => ({
type: 'ADD_TAG_SELECTOR' type: 'ADD_TAG_SELECTOR' as 'ADD_TAG_SELECTOR',
}
const addTagSelectorSync = (): AddTagSelectorAction => ({
type: 'ADD_TAG_SELECTOR',
}) })
interface RemoveTagSelectorAction { const removeTagSelectorSync = (index: number) => ({
type: 'REMOVE_TAG_SELECTOR' type: 'REMOVE_TAG_SELECTOR' as 'REMOVE_TAG_SELECTOR',
payload: {index: number}
}
const removeTagSelectorSync = (index: number): RemoveTagSelectorAction => ({
type: 'REMOVE_TAG_SELECTOR',
payload: {index}, payload: {index},
}) })
interface SetFunctionsAction { export const setFunctions = (functions: BuilderFunctionsType[]) => ({
type: 'SELECT_BUILDER_FUNCTION' type: 'SELECT_BUILDER_FUNCTION' as 'SELECT_BUILDER_FUNCTION',
payload: {functions: BuilderFunctionsType[]}
}
export const setFunctions = (
functions: BuilderFunctionsType[]
): SetFunctionsAction => ({
type: 'SELECT_BUILDER_FUNCTION',
payload: {functions}, payload: {functions},
}) })
interface SelectAggregateWindowAction { export const selectAggregateWindow = (period: string) => ({
type: 'SELECT_AGGREGATE_WINDOW' type: 'SELECT_AGGREGATE_WINDOW' as 'SELECT_AGGREGATE_WINDOW',
payload: {period: string}
}
export const selectAggregateWindow = (
period: string
): SelectAggregateWindowAction => ({
type: 'SELECT_AGGREGATE_WINDOW',
payload: {period}, payload: {period},
}) })
interface SetValuesSearchTermAction { export const setValuesSearchTerm = (index: number, searchTerm: string) => ({
type: 'SET_BUILDER_VALUES_SEARCH_TERM' type: 'SET_BUILDER_VALUES_SEARCH_TERM' as 'SET_BUILDER_VALUES_SEARCH_TERM',
payload: {index: number; searchTerm: string}
}
interface SetKeysSearchTermAction {
type: 'SET_BUILDER_KEYS_SEARCH_TERM'
payload: {index: number; searchTerm: string}
}
export const setValuesSearchTerm = (
index: number,
searchTerm: string
): SetValuesSearchTermAction => ({
type: 'SET_BUILDER_VALUES_SEARCH_TERM',
payload: {index, searchTerm}, payload: {index, searchTerm},
}) })
export const setKeysSearchTerm = ( export const setKeysSearchTerm = (index: number, searchTerm: string) => ({
index: number, type: 'SET_BUILDER_KEYS_SEARCH_TERM' as 'SET_BUILDER_KEYS_SEARCH_TERM',
searchTerm: string
): SetKeysSearchTermAction => ({
type: 'SET_BUILDER_KEYS_SEARCH_TERM',
payload: {index, searchTerm}, payload: {index, searchTerm},
}) })

View File

@ -20,11 +20,12 @@ import {ErrorHandling} from 'src/shared/decorators/errors'
// Actions // Actions
import { import {
removeTagSelector,
searchTagKeys,
searchTagValues,
selectTagKey, selectTagKey,
selectTagValue, selectTagValue,
searchTagValues, setBuilderAggregateFunctionType,
searchTagKeys,
removeTagSelector,
setKeysSearchTerm, setKeysSearchTerm,
setValuesSearchTerm, setValuesSearchTerm,
} from 'src/timeMachine/actions/queryBuilder' } from 'src/timeMachine/actions/queryBuilder'
@ -40,10 +41,12 @@ import {
// Types // Types
import {AppState, RemoteDataState} from 'src/types' import {AppState, RemoteDataState} from 'src/types'
import {BuilderAggregateFunctionType} from 'src/client'
const SEARCH_DEBOUNCE_MS = 500 const SEARCH_DEBOUNCE_MS = 500
interface StateProps { interface StateProps {
aggregateFunctionType: BuilderAggregateFunctionType
emptyText: string emptyText: string
keys: string[] keys: string[]
keysStatus: RemoteDataState keysStatus: RemoteDataState
@ -57,13 +60,14 @@ interface StateProps {
} }
interface DispatchProps { interface DispatchProps {
onSelectValue: typeof selectTagValue
onSelectTag: typeof selectTagKey
onSearchValues: typeof searchTagValues
onSearchKeys: typeof searchTagKeys
onRemoveTagSelector: typeof removeTagSelector onRemoveTagSelector: typeof removeTagSelector
onSetValuesSearchTerm: typeof setValuesSearchTerm onSearchKeys: typeof searchTagKeys
onSearchValues: typeof searchTagValues
onSelectTag: typeof selectTagKey
onSelectValue: typeof selectTagValue
onSetBuilderAggregateFunctionType: typeof setBuilderAggregateFunctionType
onSetKeysSearchTerm: typeof setKeysSearchTerm onSetKeysSearchTerm: typeof setKeysSearchTerm
onSetValuesSearchTerm: typeof setValuesSearchTerm
} }
interface OwnProps { interface OwnProps {
@ -76,13 +80,24 @@ type Props = StateProps & DispatchProps & OwnProps
class TagSelector extends PureComponent<Props> { class TagSelector extends PureComponent<Props> {
private debouncer = new DefaultDebouncer() private debouncer = new DefaultDebouncer()
// bucky: this will currently always be 'Filter'
// updates to this are imminent
private renderAggregateFunctionType(
aggregateFunctionType: BuilderAggregateFunctionType
) {
if (aggregateFunctionType === 'group') {
return 'Group'
}
return 'Filter'
}
public render() { public render() {
const {index} = this.props const {aggregateFunctionType, index} = this.props
return ( return (
<BuilderCard> <BuilderCard>
<BuilderCard.Header <BuilderCard.Header
title="Filter" title={this.renderAggregateFunctionType(aggregateFunctionType)}
onDelete={index !== 0 && this.handleRemoveTagSelector} onDelete={index !== 0 && this.handleRemoveTagSelector}
/> />
{this.body} {this.body}
@ -255,15 +270,19 @@ class TagSelector extends PureComponent<Props> {
const mstp = (state: AppState, ownProps: OwnProps): StateProps => { const mstp = (state: AppState, ownProps: OwnProps): StateProps => {
const { const {
keys, keys,
keysSearchTerm,
keysStatus, keysStatus,
values, values,
valuesStatus,
valuesSearchTerm, valuesSearchTerm,
keysSearchTerm, valuesStatus,
} = getActiveTimeMachine(state).queryBuilder.tags[ownProps.index] } = getActiveTimeMachine(state).queryBuilder.tags[ownProps.index]
const tags = getActiveQuery(state).builderConfig.tags const tags = getActiveQuery(state).builderConfig.tags
const {key: selectedKey, values: selectedValues} = tags[ownProps.index] const {
key: selectedKey,
values: selectedValues,
aggregateFunctionType,
} = tags[ownProps.index]
let emptyText: string let emptyText: string
@ -276,6 +295,7 @@ const mstp = (state: AppState, ownProps: OwnProps): StateProps => {
const isInCheckOverlay = getIsInCheckOverlay(state) const isInCheckOverlay = getIsInCheckOverlay(state)
return { return {
aggregateFunctionType,
emptyText, emptyText,
keys, keys,
keysStatus, keysStatus,
@ -290,11 +310,12 @@ const mstp = (state: AppState, ownProps: OwnProps): StateProps => {
} }
const mdtp = { const mdtp = {
onSelectValue: selectTagValue,
onSelectTag: selectTagKey,
onSearchValues: searchTagValues,
onSearchKeys: searchTagKeys,
onRemoveTagSelector: removeTagSelector, onRemoveTagSelector: removeTagSelector,
onSearchKeys: searchTagKeys,
onSearchValues: searchTagValues,
onSelectTag: selectTagKey,
onSelectValue: selectTagValue,
onSetBuilderAggregateFunctionType: setBuilderAggregateFunctionType,
onSetKeysSearchTerm: setKeysSearchTerm, onSetKeysSearchTerm: setKeysSearchTerm,
onSetValuesSearchTerm: setValuesSearchTerm, onSetValuesSearchTerm: setValuesSearchTerm,
} }

View File

@ -0,0 +1,42 @@
import {createStore} from 'redux'
import {
initialState,
initialStateHelper,
timeMachinesReducer,
} from 'src/timeMachine/reducers'
describe('the Time Machine reducer', () => {
describe('setting the default aggregateFunctionType', () => {
const store = createStore(timeMachinesReducer, initialState())
const expectedAggregatefunctionType = initialStateHelper().queryBuilder
.tags[0].aggregateFunctionType
it('is set when setting a builder bucket selection', () => {
store.dispatch({
type: 'SET_BUILDER_BUCKET_SELECTION',
payload: {bucket: 'foo', resetSelections: true},
})
const actualState = store.getState()
const defaultAggregateFunctionType =
actualState.timeMachines.de.draftQueries[0].builderConfig.tags[0]
.aggregateFunctionType
expect(defaultAggregateFunctionType).toEqual(
expectedAggregatefunctionType
)
})
it('is set when adding a new tag selector', () => {
store.dispatch({type: 'ADD_TAG_SELECTOR'})
const actualState = store.getState()
const defaultAggregateFunctionType =
actualState.timeMachines.de.draftQueries[0].builderConfig.tags[0]
.aggregateFunctionType
expect(defaultAggregateFunctionType).toEqual(
expectedAggregatefunctionType
)
})
})
})

View File

@ -41,6 +41,7 @@ import {Action} from 'src/timeMachine/actions'
import {TimeMachineTab} from 'src/types/timeMachine' import {TimeMachineTab} from 'src/types/timeMachine'
import {RemoteDataState, TimeMachineID} from 'src/types' import {RemoteDataState, TimeMachineID} from 'src/types'
import {Color} from 'src/types/colors' import {Color} from 'src/types/colors'
import {BuilderAggregateFunctionType} from 'src/client/generatedRoutes'
interface QueryBuilderState { interface QueryBuilderState {
buckets: string[] buckets: string[]
@ -48,6 +49,7 @@ interface QueryBuilderState {
functions: Array<[{name: string}]> functions: Array<[{name: string}]>
aggregateWindow: BuilderConfigAggregateWindow aggregateWindow: BuilderConfigAggregateWindow
tags: Array<{ tags: Array<{
aggregateFunctionType: BuilderAggregateFunctionType
valuesSearchTerm: string valuesSearchTerm: string
keysSearchTerm: string keysSearchTerm: string
keys: string[] keys: string[]
@ -115,11 +117,12 @@ export const initialStateHelper = (): TimeMachineState => ({
functions: [], functions: [],
tags: [ tags: [
{ {
valuesSearchTerm: '', aggregateFunctionType: 'filter',
keysSearchTerm: '',
keys: [], keys: [],
keysSearchTerm: '',
keysStatus: RemoteDataState.NotStarted, keysStatus: RemoteDataState.NotStarted,
values: [], values: [],
valuesSearchTerm: '',
valuesStatus: RemoteDataState.NotStarted, valuesStatus: RemoteDataState.NotStarted,
}, },
], ],
@ -649,6 +652,23 @@ export const timeMachineReducer = (
}) })
} }
case 'SET_BUILDER_AGGREGATE_FUNCTION_TYPE': {
return produce(state, draftState => {
const {index, builderAggregateFunctionType} = action.payload
const draftQuery = draftState.draftQueries[draftState.activeQueryIndex]
if (
draftQuery &&
draftQuery.builderConfig &&
draftQuery.builderConfig.tags[index]
) {
draftQuery.builderConfig.tags[
index
].aggregateFunctionType = builderAggregateFunctionType
}
})
}
case 'SET_BUILDER_BUCKET_SELECTION': { case 'SET_BUILDER_BUCKET_SELECTION': {
return produce(state, draftState => { return produce(state, draftState => {
const builderConfig = const builderConfig =
@ -657,7 +677,16 @@ export const timeMachineReducer = (
builderConfig.buckets = [action.payload.bucket] builderConfig.buckets = [action.payload.bucket]
if (action.payload.resetSelections) { if (action.payload.resetSelections) {
builderConfig.tags = [{key: '', values: []}] const defaultAggregateFunctionType = initialStateHelper().queryBuilder
.tags[0].aggregateFunctionType
builderConfig.tags = [
{
key: '',
values: [],
aggregateFunctionType: defaultAggregateFunctionType,
},
]
buildActiveQuery(draftState) buildActiveQuery(draftState)
} }
}) })
@ -770,16 +799,14 @@ export const timeMachineReducer = (
case 'ADD_TAG_SELECTOR': { case 'ADD_TAG_SELECTOR': {
return produce(state, draftState => { return produce(state, draftState => {
const draftQuery = draftState.draftQueries[draftState.activeQueryIndex] const draftQuery = draftState.draftQueries[draftState.activeQueryIndex]
const [initialTags] = initialStateHelper().queryBuilder.tags
draftQuery.builderConfig.tags.push({key: '', values: []}) draftQuery.builderConfig.tags.push({
draftState.queryBuilder.tags.push({ key: '',
valuesSearchTerm: '',
keysSearchTerm: '',
keys: [],
keysStatus: RemoteDataState.NotStarted,
values: [], values: [],
valuesStatus: RemoteDataState.NotStarted, aggregateFunctionType: initialTags.aggregateFunctionType,
}) })
draftState.queryBuilder.tags.push(initialTags)
}) })
} }
@ -1066,14 +1093,7 @@ const initialQueryBuilderState = (
bucketsStatus: RemoteDataState.NotStarted, bucketsStatus: RemoteDataState.NotStarted,
functions: [], functions: [],
aggregateWindow: {period: 'auto'}, aggregateWindow: {period: 'auto'},
tags: builderConfig.tags.map(_ => ({ tags: initialStateHelper().queryBuilder.tags,
valuesSearchTerm: '',
keysSearchTerm: '',
keys: [],
keysStatus: RemoteDataState.NotStarted,
values: [],
valuesStatus: RemoteDataState.NotStarted,
})),
} }
} }