diff --git a/static/app/components/metrics/metricSearchBar.tsx b/static/app/components/metrics/metricSearchBar.tsx
index 8e39cc11dfbdd..dd80cf5178613 100644
--- a/static/app/components/metrics/metricSearchBar.tsx
+++ b/static/app/components/metrics/metricSearchBar.tsx
@@ -1,14 +1,21 @@
import {useCallback, useMemo} from 'react';
-import {css} from '@emotion/react';
+import {css, type SerializedStyles} from '@emotion/react';
import {useId} from '@react-aria/utils';
import {QueryFieldGroup} from 'sentry/components/metrics/queryFieldGroup';
+import {
+ SearchQueryBuilder,
+ type SearchQueryBuilderProps,
+} from 'sentry/components/searchQueryBuilder';
import type {SmartSearchBarProps} from 'sentry/components/smartSearchBar';
import SmartSearchBar from 'sentry/components/smartSearchBar';
import {t} from 'sentry/locale';
import {SavedSearchType, type TagCollection} from 'sentry/types/group';
import type {MRI} from 'sentry/types/metrics';
-import {hasMetricsNewInputs} from 'sentry/utils/metrics/features';
+import {
+ hasMetricsNewInputs,
+ hasMetricsNewSearchQueryBuilder,
+} from 'sentry/utils/metrics/features';
import {getUseCaseFromMRI} from 'sentry/utils/metrics/mri';
import {useMetricsTags} from 'sentry/utils/metrics/useMetricsTags';
import useApi from 'sentry/utils/useApi';
@@ -40,7 +47,7 @@ export function MetricSearchBar({
id: idProp,
...props
}: MetricSearchBarProps) {
- const org = useOrganization();
+ const organization = useOrganization();
const api = useApi();
const {selection} = usePageFilters();
const selectedProjects = useSelectedProjects();
@@ -82,16 +89,19 @@ export function MetricSearchBar({
const fetchTagValues = useCallback(
(tagKey: string, search: string) => {
- return api.requestPromise(`/organizations/${org.slug}/metrics/tags/${tagKey}/`, {
- query: {
- prefix: search,
- metric: mri,
- useCase: getUseCaseFromMRI(mri),
- project: selection.projects,
- },
- });
+ return api.requestPromise(
+ `/organizations/${organization.slug}/metrics/tags/${tagKey}/`,
+ {
+ query: {
+ prefix: search,
+ metric: mri,
+ useCase: getUseCaseFromMRI(mri),
+ project: selection.projects,
+ },
+ }
+ );
},
- [api, mri, org.slug, selection.projects]
+ [api, mri, organization.slug, selection.projects]
);
const getTagValues = useCallback(
@@ -118,47 +128,52 @@ export function MetricSearchBar({
[onChange, searchConfig]
);
- if (hasMetricsNewInputs(org)) {
- return (
-
- );
+ const searchQueryBuilderProps: SearchQueryBuilderProps & {css: SerializedStyles} = {
+ disabled,
+ onChange: handleChange,
+ placeholder: t('Filter by tags'),
+ initialQuery: query ?? '',
+ getTagValues,
+ recentSearches: SavedSearchType.METRIC,
+ // don't highlight tags while loading as we don't know yet if they are supported
+ disallowUnsupportedFilters: !isLoading,
+ filterKeys: searchConfig.supportedTags,
+ disallowFreeText: searchConfig.disallowFreeText,
+ searchSource: props.searchSource ?? 'metrics',
+ css: wideSearchBarCss(disabled),
+ };
+
+ const smartSearchProps: Partial & {css: SerializedStyles} = {
+ id,
+ disabled,
+ maxMenuHeight: 220,
+ organization,
+ onGetTagValues: getTagValues,
+ // don't highlight tags while loading as we don't know yet if they are supported
+ highlightUnsupportedTags: !isLoading,
+ onClose: handleChange,
+ onSearch: handleChange,
+ placeholder: t('Filter by tags'),
+ query,
+ savedSearchType: SavedSearchType.METRIC,
+ css: wideSearchBarCss(disabled),
+ ...props,
+ ...searchConfig,
+ };
+
+ if (hasMetricsNewInputs(organization)) {
+ if (hasMetricsNewSearchQueryBuilder(organization)) {
+ return ;
+ }
+
+ return ;
}
- return (
-
- );
+ if (hasMetricsNewSearchQueryBuilder(organization)) {
+ return ;
+ }
+
+ return ;
}
function wideSearchBarCss(disabled?: boolean) {
diff --git a/static/app/components/metrics/queryFieldGroup.tsx b/static/app/components/metrics/queryFieldGroup.tsx
index e0e55b97ad580..3bdef0ebe16a4 100644
--- a/static/app/components/metrics/queryFieldGroup.tsx
+++ b/static/app/components/metrics/queryFieldGroup.tsx
@@ -1,3 +1,4 @@
+import type {Theme} from '@emotion/react';
import {css, useTheme} from '@emotion/react';
import styled from '@emotion/styled';
@@ -10,6 +11,7 @@ import {
type SingleSelectProps,
} from 'sentry/components/compactSelect';
import {DebouncedInput as _DebouncedInput} from 'sentry/components/modals/metricWidgetViewerModal/queries';
+import {SearchQueryBuilder as _SearchQueryBuilder} from 'sentry/components/searchQueryBuilder';
import _SmartSearchBar from 'sentry/components/smartSearchBar';
import {Tooltip} from 'sentry/components/tooltip';
import {SLOW_TOOLTIP_DELAY} from 'sentry/constants';
@@ -97,17 +99,25 @@ const ComboBox = styled(_ComboBox)`
}
`;
-const SmartSearchBar = styled(_SmartSearchBar)`
+const searchCss = (theme: Theme) => css`
border-radius: 0;
:last-child {
- border-radius: 0 ${p => p.theme.borderRadius} ${p => p.theme.borderRadius} 0;
+ border-radius: 0 ${theme.borderRadius} ${theme.borderRadius} 0;
}
label {
- color: ${p => p.theme.gray500};
+ color: ${theme.gray500};
}
`;
+const SmartSearchBar = styled(_SmartSearchBar)`
+ ${p => searchCss(p.theme)}
+`;
+
+const SearchQueryBuilder = styled(_SearchQueryBuilder)`
+ ${p => searchCss(p.theme)}
+`;
+
const FieldGroup = styled('div')`
flex-grow: 1;
@@ -155,5 +165,6 @@ QueryFieldGroup.Label = Label;
QueryFieldGroup.CompactSelect = CompactSelect;
QueryFieldGroup.ComboBox = ComboBox;
QueryFieldGroup.SmartSearchBar = SmartSearchBar;
+QueryFieldGroup.SearchQueryBuilder = SearchQueryBuilder;
QueryFieldGroup.DebouncedInput = DebouncedInput;
QueryFieldGroup.DeleteButton = DeleteButton;
diff --git a/static/app/utils/metrics/features.tsx b/static/app/utils/metrics/features.tsx
index 4f11ff5ca3b21..c2682ebfe556f 100644
--- a/static/app/utils/metrics/features.tsx
+++ b/static/app/utils/metrics/features.tsx
@@ -28,6 +28,10 @@ export function hasMetricsNewInputs(organization: Organization) {
return organization.features.includes('metrics-new-inputs');
}
+export function hasMetricsNewSearchQueryBuilder(organization: Organization) {
+ return organization.features.includes('search-query-builder-metrics');
+}
+
/**
* Returns the forceMetricsLayer query param for the alert
* wrapped in an object so it can be spread into existing query params