import {Fragment, useState} from 'react';
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 {ItemType} from 'sentry/components/smartSearchBar/types';
import JSXNode from 'sentry/components/stories/jsxNode';
import JSXProperty from 'sentry/components/stories/jsxProperty';
import storyBook from 'sentry/stories/storyBook';
import type {TagCollection} from 'sentry/types/group';
import {
FieldKey,
FieldKind,
FieldValueType,
MobileVital,
WebVital,
} from 'sentry/utils/fields';
const FILTER_KEYS: TagCollection = {
[FieldKey.ASSIGNED]: {
key: FieldKey.ASSIGNED,
name: 'Assigned To',
kind: FieldKind.FIELD,
predefined: true,
values: [
{
title: 'Suggested',
type: 'header',
icon: null,
children: [{value: 'me'}, {value: 'unassigned'}],
},
{
title: 'All',
type: 'header',
icon: null,
children: [{value: 'person1@sentry.io'}, {value: 'person2@sentry.io'}],
},
],
},
[FieldKey.BROWSER_NAME]: {
key: FieldKey.BROWSER_NAME,
name: 'Browser Name',
kind: FieldKind.FIELD,
predefined: true,
values: ['Chrome', 'Firefox', 'Safari', 'Edge'],
},
[FieldKey.IS]: {
key: FieldKey.IS,
name: 'is',
predefined: true,
values: ['resolved', 'unresolved', 'ignored'],
},
[FieldKey.LAST_SEEN]: {
key: FieldKey.LAST_SEEN,
name: 'lastSeen',
kind: FieldKind.FIELD,
},
[FieldKey.TIMES_SEEN]: {
key: FieldKey.TIMES_SEEN,
name: 'timesSeen',
kind: FieldKind.FIELD,
},
[WebVital.LCP]: {
key: WebVital.LCP,
name: 'lcp',
kind: FieldKind.FIELD,
},
[MobileVital.FRAMES_SLOW_RATE]: {
key: MobileVital.FRAMES_SLOW_RATE,
name: 'framesSlowRate',
kind: FieldKind.FIELD,
},
custom_tag_name: {
key: 'custom_tag_name',
name: 'Custom_Tag_Name',
},
};
const FITLER_KEY_SECTIONS: FilterKeySection[] = [
{
value: 'cat_1',
label: 'Category 1',
children: [
FieldKey.ASSIGNED,
FieldKey.BROWSER_NAME,
FieldKey.IS,
FieldKey.LAST_SEEN,
FieldKey.TIMES_SEEN,
],
},
{
value: 'cat_2',
label: 'Category 2',
children: [WebVital.LCP, MobileVital.FRAMES_SLOW_RATE],
},
{
value: 'cat_3',
label: 'Category 3',
children: ['custom_tag_name'],
},
];
const getTagValues = (): Promise
The search query, unless configured otherwise, may contain filters, logical
operators, and free text. These filters can have defined data types, but default
to a multi-selectable string filter.
Required props:
initialQuery
: The initial query to display in the search input.
filterKeys
: A collection of filter keys which are used to populate the dropdowns. All
valid filter keys should be defined here.
getTagValues
: A function which returns an array of filter value suggestions. Any filter
key which does not have predefined: true
will use this function
to get value suggestions.
searchSource
: Used to differentiate between different search bars for analytics.
Typically snake_case (e.g. issue_details
,{' '}
performance_landing
).
To guide the user in building a search query, filter value suggestions can be provided in a few different ways:
filterKeys
. These
suggestions can also be formatted:
values
.
values
. Each object should
have a title
and children
array.
ItemType.TAG_VALUE
with a{' '}
documentation
property.
predefined: true
, it will use the getTagValues
{' '}
function to fetch suggestions. The filter key and query are provided, and it
is up to the consumer to return the suggestions.
A special menu can be displayed when no text is entered in the search input, allowing for better oranization and discovery of filter keys.
This menu is defined by filterKeySections
, which accepts a list of
sections. Each section contains a name and a list of filter keys. Note that the
order of both the sections and the items within each section are respected.
Field definitions very important for the search query builder to work correctly. They provide information such as what data types are allow for a given filter, as well as the description and keywords.
By default, field definitions are sourced from{' '}
EVENT_FIELD_DEFINITIONS
in sentry/utils/fields.ts
. If
these definitions are not correct for the use case, they can be overridden by
passing fieldDefinitionGetter
.
onChange
is called whenever the search query changes. This can be
used to update the UI as the user updates the query.
onSearch
is called when the user presses enter. This can be used to
submit the search query.
onChange
value
: {onChangeValue}
onSearch
value
: {onSearchValue}
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. Use the checkboxes below to enable/disable the following options:
{config}
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{' '}
Some props have been renamed:
supportedTags
{'->'} filterKeys
onGetTagValues
{'->'} getTagValues
highlightUnsupportedTags
{'->'}{' '}
disallowUnsupportedFilters
savedSearchType
{'->'} recentSearches
Some props have been removed:
excludedTags
is no longer supported. If a filter key should not
be shown, do not include it in filterKeys
.
(boolean|date|duration)Keys
no longer need to be specified. The
filter value types are inferred from the field definitions.
projectIds
was used to add is_multi_project
to
some of the analytics events. If your use case requires this, you can record
these events manually with the onSearch
callback.
hasRecentSearches
is no longer required. Saved searches will be
saved and displayed when recentSearches
is provided.