Skip to content

Commit

Permalink
feat(query-builder): Add basic support for selecting multiple values
Browse files Browse the repository at this point in the history
  • Loading branch information
malwilley committed May 23, 2024
1 parent 64d894d commit 0766201
Show file tree
Hide file tree
Showing 3 changed files with 282 additions and 44 deletions.
2 changes: 1 addition & 1 deletion static/app/components/searchQueryBuilder/filter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ function FilterValue({token, state, item}: SearchQueryTokenProps) {
<ValueEditing ref={ref} {...mergeProps(focusWithinProps, filterButtonProps)}>
<SearchQueryBuilderValueCombobox
token={token}
onChange={() => {
onCommit={() => {
setIsEditing(false);
if (state.collection.getKeyAfter(item.key)) {
state.selectionManager.setFocusedKey(
Expand Down
85 changes: 83 additions & 2 deletions static/app/components/searchQueryBuilder/useQueryBuilderState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {type Reducer, useCallback, useReducer} from 'react';
import {
type ParseResultToken,
TermOperator,
type Token,
Token,
type TokenResult,
} from 'sentry/components/searchSyntax/parser';
import {stringifyToken} from 'sentry/components/searchSyntax/utils';
Expand Down Expand Up @@ -35,11 +35,24 @@ type UpdateTokenValueAction = {
value: string;
};

type MultiSelectFilterValueAction = {
token: TokenResult<Token.FILTER>;
type: 'TOGGLE_FILTER_VALUE';
value: string;
};

type DeleteLastMultiSelectFilterValueAction = {
token: TokenResult<Token.FILTER>;
type: 'DELETE_LAST_MULTI_SELECT_FILTER_VALUE';
};

export type QueryBuilderActions =
| DeleteTokenAction
| UpdateFreeTextAction
| UpdateFilterOpAction
| UpdateTokenValueAction;
| UpdateTokenValueAction
| MultiSelectFilterValueAction
| DeleteLastMultiSelectFilterValueAction;

function removeQueryToken(query: string, token: TokenResult<Token>): string {
return (
Expand Down Expand Up @@ -101,6 +114,70 @@ function updateFreeText(
};
}

function updateFilterMultipleValues(
state: QueryBuilderState,
token: TokenResult<Token.FILTER>,
values: string[]
) {
const uniqNonEmptyValues = Array.from(
new Set(values.filter(value => value.length > 0))
);
if (uniqNonEmptyValues.length === 0) {
return {...state, query: replaceQueryToken(state.query, token.value, '')};
}

const newValue =
uniqNonEmptyValues.length > 1
? `[${uniqNonEmptyValues.join(',')}]`
: uniqNonEmptyValues[0];

return {...state, query: replaceQueryToken(state.query, token.value, newValue)};
}

function mutliSelectTokenValue(
state: QueryBuilderState,
action: MultiSelectFilterValueAction
) {
const tokenValue = action.token.value;

switch (tokenValue.type) {
case Token.VALUE_TEXT_LIST:
case Token.VALUE_NUMBER_LIST:
const values = tokenValue.items.map(item => item.value?.text ?? '');
const containsValue = values.includes(action.value);
const newValues = containsValue
? values.filter(value => value !== action.value)
: [...values, action.value];

return updateFilterMultipleValues(state, action.token, newValues);
default:
if (tokenValue.text === action.value) {
return updateFilterMultipleValues(state, action.token, ['']);
}
return updateFilterMultipleValues(state, action.token, [
tokenValue.text,
action.value,
]);
}
}

function deleteLastMutliSelectTokenValue(
state: QueryBuilderState,
action: DeleteLastMultiSelectFilterValueAction
) {
const tokenValue = action.token.value;

switch (tokenValue.type) {
case Token.VALUE_TEXT_LIST:
case Token.VALUE_NUMBER_LIST:
const newValues = tokenValue.items.slice(0, -1).map(item => item.value?.text ?? '');

return updateFilterMultipleValues(state, action.token, newValues);
default:
return updateFilterMultipleValues(state, action.token, ['']);
}
}

export function useQueryBuilderState({initialQuery}: {initialQuery: string}) {
const initialState: QueryBuilderState = {query: initialQuery};

Expand All @@ -121,6 +198,10 @@ export function useQueryBuilderState({initialQuery}: {initialQuery: string}) {
return {
query: replaceQueryToken(state.query, action.token, action.value),
};
case 'TOGGLE_FILTER_VALUE':
return mutliSelectTokenValue(state, action);
case 'DELETE_LAST_MULTI_SELECT_FILTER_VALUE':
return deleteLastMutliSelectTokenValue(state, action);
default:
return state;
}
Expand Down
Loading

0 comments on commit 0766201

Please sign in to comment.