From add494e9abfe88b80785c6bfa04b1805ab925542 Mon Sep 17 00:00:00 2001 From: Malachi Willey Date: Tue, 23 Jul 2024 12:35:06 -0700 Subject: [PATCH] feat(query-builder): Add ability to customize invalid token messages --- .../searchQueryBuilder/index.spec.tsx | 24 ++++++++++++++++++ .../searchQueryBuilder/index.stories.tsx | 25 ++++++++++++++++++- .../components/searchQueryBuilder/index.tsx | 14 ++++++++--- .../components/searchQueryBuilder/utils.tsx | 2 ++ 4 files changed, 61 insertions(+), 4 deletions(-) diff --git a/static/app/components/searchQueryBuilder/index.spec.tsx b/static/app/components/searchQueryBuilder/index.spec.tsx index 795b4b670cef39..d13e8b58835ab7 100644 --- a/static/app/components/searchQueryBuilder/index.spec.tsx +++ b/static/app/components/searchQueryBuilder/index.spec.tsx @@ -19,6 +19,7 @@ import { QueryInterfaceType, } from 'sentry/components/searchQueryBuilder/types'; import {INTERFACE_TYPE_LOCALSTORAGE_KEY} from 'sentry/components/searchQueryBuilder/utils'; +import {InvalidReason} from 'sentry/components/searchSyntax/parser'; import type {TagCollection} from 'sentry/types/group'; import {FieldKey, FieldKind, FieldValueType} from 'sentry/utils/fields'; import localStorageWrapper from 'sentry/utils/localStorage'; @@ -2051,4 +2052,27 @@ describe('SearchQueryBuilder', function () { ).toBeInTheDocument(); }); }); + + describe('invalidMessages', function () { + it('should customize invalid messages', async function () { + render( + + ); + + expect(screen.getByRole('row', {name: 'foo:'})).toHaveAttribute( + 'aria-invalid', + 'true' + ); + + await userEvent.click(getLastInput()); + await userEvent.keyboard('{ArrowLeft}'); + expect(await screen.findByText('foo bar baz')).toBeInTheDocument(); + }); + }); }); diff --git a/static/app/components/searchQueryBuilder/index.stories.tsx b/static/app/components/searchQueryBuilder/index.stories.tsx index f96f564a154d30..d6875b1b78e788 100644 --- a/static/app/components/searchQueryBuilder/index.stories.tsx +++ b/static/app/components/searchQueryBuilder/index.stories.tsx @@ -5,6 +5,8 @@ import Alert from 'sentry/components/alert'; import MultipleCheckbox from 'sentry/components/forms/controls/multipleCheckbox'; import {SearchQueryBuilder} from 'sentry/components/searchQueryBuilder'; import type {FilterKeySection} from 'sentry/components/searchQueryBuilder/types'; +import {InvalidReason} from 'sentry/components/searchSyntax/parser'; +import JSXProperty from 'sentry/components/stories/jsxProperty'; import SizingWindow from 'sentry/components/stories/sizingWindow'; import storyBook from 'sentry/stories/storyBook'; import type {TagCollection} from 'sentry/types/group'; @@ -132,7 +134,8 @@ export default storyBook(SearchQueryBuilder, story => {

There are some config options which allow you to customize which types of syntax are considered valid. This should be used when the search backend does not - support certain operators like boolean logic or wildcards. + support certain operators like boolean logic or wildcards. Use the checkboxes + below to enable/disable the following options:

{ searchSource="storybook" {...queryBuilderOptions} /> +

+ The query above has a few invalid tokens. The invalid tokens are highlighted in + red and display a tooltip with a message when focused. The invalid token + messages can be customized using the invalidMessages prop. In this + case, the unsupported tag message is modified with{' '} + + . +

+ ); }); diff --git a/static/app/components/searchQueryBuilder/index.tsx b/static/app/components/searchQueryBuilder/index.tsx index d45597dd586c6e..a31355f4eea491 100644 --- a/static/app/components/searchQueryBuilder/index.tsx +++ b/static/app/components/searchQueryBuilder/index.tsx @@ -17,6 +17,7 @@ import { QueryInterfaceType, } from 'sentry/components/searchQueryBuilder/types'; import {parseQueryBuilderValue} from 'sentry/components/searchQueryBuilder/utils'; +import type {SearchConfig} from 'sentry/components/searchSyntax/parser'; import {IconClose, IconSearch} from 'sentry/icons'; import {t} from 'sentry/locale'; import {space} from 'sentry/styles/space'; @@ -67,6 +68,10 @@ export interface SearchQueryBuilderProps { * Sections and filter keys are displayed in the order they are provided. */ filterKeySections?: FilterKeySection[]; + /** + * Allows for customization of the invalid token messages. + */ + invalidMessages?: SearchConfig['invalidMessages']; label?: string; onBlur?: (query: string) => void; /** @@ -107,6 +112,7 @@ export function SearchQueryBuilder({ disallowFreeText, disallowUnsupportedFilters, disallowWildcard, + invalidMessages, label, initialQuery, fieldDefinitionGetter = getFieldDefinition, @@ -132,15 +138,17 @@ export function SearchQueryBuilder({ disallowUnsupportedFilters, disallowWildcard, filterKeys, + invalidMessages, }), [ + state.query, + fieldDefinitionGetter, disallowFreeText, disallowLogicalOperators, + disallowUnsupportedFilters, disallowWildcard, - fieldDefinitionGetter, filterKeys, - disallowUnsupportedFilters, - state.query, + invalidMessages, ] ); diff --git a/static/app/components/searchQueryBuilder/utils.tsx b/static/app/components/searchQueryBuilder/utils.tsx index 6506d5dd1d18f8..9869fcbec92b79 100644 --- a/static/app/components/searchQueryBuilder/utils.tsx +++ b/static/app/components/searchQueryBuilder/utils.tsx @@ -69,6 +69,7 @@ export function parseQueryBuilderValue( disallowLogicalOperators?: boolean; disallowUnsupportedFilters?: boolean; disallowWildcard?: boolean; + invalidMessages?: SearchConfig['invalidMessages']; } ): ParseResult | null { return collapseTextTokens( @@ -82,6 +83,7 @@ export function parseQueryBuilderValue( : undefined, disallowParens: options?.disallowLogicalOperators, ...getSearchConfigFromKeys(options?.filterKeys ?? {}, getFieldDefinition), + invalidMessages: options?.invalidMessages, supportedTags: options?.filterKeys, }) );