Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 39 additions & 32 deletions static/app/actionCreators/savedSearches.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import {skipToken, useQuery, type UseQueryOptions} from '@tanstack/react-query';

import type {Client} from 'sentry/api';
import {MAX_AUTOCOMPLETE_RECENT_SEARCHES} from 'sentry/constants';
import type {RecentSearch, SavedSearch, SavedSearchType} from 'sentry/types/group';
import type {ApiResponse} from 'sentry/utils/api/apiFetch';
import {apiOptions} from 'sentry/utils/api/apiOptions';
import type {ApiQueryKey} from 'sentry/utils/api/apiQueryKey';
import {getApiUrl} from 'sentry/utils/api/getApiUrl';
import {defined} from 'sentry/utils/defined';
import {handleXhrErrorResponse} from 'sentry/utils/handleXhrErrorResponse';
import {useApiQuery, type UseApiQueryOptions} from 'sentry/utils/queryClient';
import type {RequestError} from 'sentry/utils/requestError/requestError';
import {useOrganization} from 'sentry/utils/useOrganization';

Expand Down Expand Up @@ -77,29 +79,45 @@ export function saveRecentSearch(
return promise;
}

function makeRecentSearchesQueryKey({
function recentSearchesApiOptions({
limit,
namespace,
orgSlug,
savedSearchType,
query,
}: {
limit: number;
orgSlug: string;
savedSearchType: SavedSearchType | null;
namespace?: string;
query?: string;
}): ApiQueryKey {
return [
getRecentSearchUrl(orgSlug),
{
query: {
query,
type: savedSearchType,
limit,
},
},
];
}) {
return {
...apiOptions.as<RecentSearch[]>()(
'/organizations/$organizationIdOrSlug/recent-searches/',
{
path: savedSearchType === null ? skipToken : {organizationIdOrSlug: orgSlug},
query: {
query: encodeNamespacedRecentSearch(namespace, query),
type: savedSearchType,
limit,
},
staleTime: 0,
}
),
select: ({json}: ApiResponse<RecentSearch[]>) =>
json.map(search => ({
...search,
query: decodeNamespacedRecentSearch(namespace, search.query),
})),
};
}

type RecentSearchesQueryOptions = Omit<
UseQueryOptions<ApiResponse<RecentSearch[]>, Error, RecentSearch[], ApiQueryKey>,
'queryKey' | 'queryFn' | 'select'
>;

export function useFetchRecentSearches(
{
query,
Expand All @@ -112,29 +130,18 @@ export function useFetchRecentSearches(
namespace?: string;
query?: string;
},
options: Partial<UseApiQueryOptions<RecentSearch[]>> = {}
options: RecentSearchesQueryOptions = {}
) {
const organization = useOrganization();

const response = useApiQuery<RecentSearch[]>(
makeRecentSearchesQueryKey({
return useQuery({
...recentSearchesApiOptions({
limit,
namespace,
orgSlug: organization.slug,
query: encodeNamespacedRecentSearch(namespace, query),
query,
savedSearchType,
}),
{
staleTime: 0,
enabled: defined(savedSearchType),
...options,
}
);

return {
...response,
data: response.data?.map(search => ({
...search,
query: decodeNamespacedRecentSearch(namespace, search.query),
})),
};
...options,
});
}
1 change: 1 addition & 0 deletions static/app/components/searchQueryBuilder/context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ export function SearchQueryBuilderProvider({

const {filterKeyRegistryQueryOptions, registerFilterKeys} = useFilterKeyRegistry({
asyncFilterKeyRegistryQueryKey,
enabled: Boolean(getTagKeys || asyncFilterKeyRegistryQueryKey),
});

const {data: asyncFilterKeys = {}} = useQuery(filterKeyRegistryQueryOptions);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import type {Tag, TagCollection} from 'sentry/types/group';

export function useFilterKeyRegistry({
asyncFilterKeyRegistryQueryKey,
enabled = true,
}: {
asyncFilterKeyRegistryQueryKey?: QueryKey;
enabled?: boolean;
}) {
const queryClient = useQueryClient();
const fallbackRegistryId = useId();
Expand All @@ -25,9 +27,10 @@ export function useFilterKeyRegistry({
queryOptions({
queryKey: filterKeyRegistryQueryKey,
queryFn: () => ({}),
enabled,
staleTime: Infinity,
}),
[filterKeyRegistryQueryKey]
[enabled, filterKeyRegistryQueryKey]
);

const registerFilterKeys = useCallback(
Expand Down
34 changes: 17 additions & 17 deletions static/app/components/searchQueryBuilder/tokens/combobox.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
useCallback,
useEffect,
useEffectEvent,
useLayoutEffect,
useMemo,
useRef,
Expand Down Expand Up @@ -163,6 +164,12 @@ const DESCRIPTION_POPPER_OPTIONS = {
],
};

const MENU_OFFSET: [number, number] = [-12, 12];
const MENU_FLIP_OPTIONS = {
// We don't want the menu to ever flip to the other side of the input.
fallbackPlacements: [],
};

function menuIsOpen({
state,
hiddenOptions,
Expand Down Expand Up @@ -249,20 +256,11 @@ function useUpdateOverlayPositionOnContentChange({
updateOverlayPosition: (() => void) | null;
}) {
const resizeObserverRef = useRef<ResizeObserver | null>(null);

// Keep a ref to the updateOverlayPosition function so that we can
// access the latest value in the resize observer callback.
const updateOverlayPositionRef = useRef(updateOverlayPosition);
if (updateOverlayPositionRef.current !== updateOverlayPosition) {
updateOverlayPositionRef.current = updateOverlayPosition;
}
const updatePosition = useEffectEvent(() => updateOverlayPosition?.());

useLayoutEffect(() => {
resizeObserverRef.current = new ResizeObserver(() => {
if (!updateOverlayPositionRef.current) {
return;
}
updateOverlayPositionRef.current?.();
updatePosition();
});

return () => {
Expand Down Expand Up @@ -321,6 +319,10 @@ function OverlayContent<T extends SelectOptionOrSectionWithKey<string>>({
const showAskSeerFooter =
enableAISearch && organization.features.includes('gen-ai-ask-seer-ux-rework');

if (!isOpen) {
return <StyledPositionWrapper {...overlayProps} visible={false} />;
}

if (customMenu) {
return customMenu({
popoverRef,
Expand Down Expand Up @@ -423,6 +425,7 @@ export function SearchQueryBuilderCombobox<
const popoverRef = useRef<HTMLDivElement>(null);
const descriptionRef = useRef<HTMLDivElement>(null);
const askSeerButtonRef = useRef<HTMLButtonElement>(null);
const preventOverflowOptions = useMemo(() => ({boundary: document.body}), []);

const {hiddenOptions, disabledKeys} = useHiddenItems({
items,
Expand Down Expand Up @@ -568,7 +571,7 @@ export function SearchQueryBuilderCombobox<
type: 'listbox',
isOpen,
position: 'bottom-start',
offset: [-12, 12],
offset: MENU_OFFSET,
isKeyboardDismissDisabled: true,
shouldCloseOnBlur: true,
shouldCloseOnInteractOutside: el => {
Expand Down Expand Up @@ -596,11 +599,8 @@ export function SearchQueryBuilderCombobox<
state.close();
},
shouldApplyMinWidth: false,
preventOverflowOptions: {boundary: document.body},
flipOptions: {
// We don't want the menu to ever flip to the other side of the input
fallbackPlacements: [],
},
preventOverflowOptions,
flipOptions: MENU_FLIP_OPTIONS,
});

const descriptionPopper = usePopper(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,8 @@ export function useFilterKeyListBox({filterValue}: UseFilterKeyListBoxArgs) {
const {currentInputValueRef} = useSearchQueryBuilderLayout();
const analyticsArea = useAnalyticsArea();
const {sectionedItems} = useFilterKeyItems();
const recentFilters = useRecentSearchFilters();
const {data: recentSearches} = useRecentSearches();
const recentFilters = useRecentSearchFilters(recentSearches);
const {sections, selectedSection, setSelectedSection} = useFilterKeySections({
recentSearches,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
useSearchQueryBuilderConfig,
useSearchQueryBuilderState,
} from 'sentry/components/searchQueryBuilder/context';
import {useRecentSearches} from 'sentry/components/searchQueryBuilder/tokens/filterKeyListBox/useRecentSearches';
import type {FieldDefinitionGetter} from 'sentry/components/searchQueryBuilder/types';
import {parseQueryBuilderValue} from 'sentry/components/searchQueryBuilder/utils';
import {
Expand Down Expand Up @@ -119,11 +118,10 @@ function getFiltersFromRecentSearches(
* searches but not in the current query.
* Orders by highest count of filter key occurrences.
*/
export function useRecentSearchFilters() {
export function useRecentSearchFilters(recentSearchesData: RecentSearch[] | undefined) {
const {parsedQuery} = useSearchQueryBuilderState();
const {filterKeys, getFieldDefinition, filterKeyAliases} =
useSearchQueryBuilderConfig();
const {data: recentSearchesData} = useRecentSearches();

const filters = useMemo(
() =>
Expand Down
Loading