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,
})
);