Skip to content

Commit

Permalink
fix(smart-search-bar): Debouncing dropdown updates (#62556)
Browse files Browse the repository at this point in the history
#### Solution
Move the scope of debouncing from the tag value getters to
`updateAutoCompleteItems`. That way also the calls of `setState` are
included in the debounced logic and executed on the trailing call and the
UI is updated accordingly.
  • Loading branch information
ArthurKnaus authored and armenzg committed Jan 8, 2024
1 parent b216841 commit caba48c
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 79 deletions.
147 changes: 70 additions & 77 deletions static/app/components/smartSearchBar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,7 @@ class SmartSearchBar extends Component<DefaultProps & Props, State> {

componentWillUnmount() {
this.inputResizeObserver?.disconnect();
this.updateAutoCompleteItems.cancel();
document.removeEventListener('pointerup', this.onBackgroundPointerUp);
}

Expand Down Expand Up @@ -1214,56 +1215,52 @@ class SmartSearchBar extends Component<DefaultProps & Props, State> {
* Returns array of tag values that substring match `query`; invokes `callback`
* with data when ready
*/
getTagValues = debounce(
async (tag: Tag, query: string): Promise<SearchItem[]> => {
// Strip double quotes if there are any
query = query.replace(/"/g, '').trim();
getTagValues = async (tag: Tag, query: string): Promise<SearchItem[]> => {
// Strip double quotes if there are any
query = query.replace(/"/g, '').trim();

if (!this.props.onGetTagValues) {
return [];
}
if (!this.props.onGetTagValues) {
return [];
}

if (
this.state.noValueQuery !== undefined &&
query.startsWith(this.state.noValueQuery)
) {
return [];
}
if (
this.state.noValueQuery !== undefined &&
query.startsWith(this.state.noValueQuery)
) {
return [];
}

const {location} = this.props;
const endpointParams = normalizeDateTimeParams(location.query);
const {location} = this.props;
const endpointParams = normalizeDateTimeParams(location.query);

this.setState({loading: true});
let values: string[] = [];
this.setState({loading: true});
let values: string[] = [];

try {
values = await this.props.onGetTagValues(tag, query, endpointParams);
this.setState({loading: false});
} catch (err) {
this.setState({loading: false});
Sentry.captureException(err);
return [];
}
try {
values = await this.props.onGetTagValues(tag, query, endpointParams);
this.setState({loading: false});
} catch (err) {
this.setState({loading: false});
Sentry.captureException(err);
return [];
}

if (tag.key === 'release:' && !values.includes('latest')) {
values.unshift('latest');
}
if (tag.key === 'release:' && !values.includes('latest')) {
values.unshift('latest');
}

const noValueQuery = values.length === 0 && query.length > 0 ? query : undefined;
this.setState({noValueQuery});
const noValueQuery = values.length === 0 && query.length > 0 ? query : undefined;
this.setState({noValueQuery});

return values.map(value => {
const escapedValue = escapeTagValue(value);
return {
value: escapedValue,
desc: escapedValue,
type: ItemType.TAG_VALUE,
};
});
},
DEFAULT_DEBOUNCE_DURATION,
{leading: true}
);
return values.map(value => {
const escapedValue = escapeTagValue(value);
return {
value: escapedValue,
desc: escapedValue,
type: ItemType.TAG_VALUE,
};
});
};

/**
* Returns array of tag values that substring match `query`; invokes `callback`
Expand Down Expand Up @@ -1304,21 +1301,17 @@ class SmartSearchBar extends Component<DefaultProps & Props, State> {
/**
* Get recent searches
*/
getRecentSearches = debounce(
async () => {
const {savedSearchType, hasRecentSearches, onGetRecentSearches} = this.props;
getRecentSearches = async () => {
const {savedSearchType, hasRecentSearches, onGetRecentSearches} = this.props;

// `savedSearchType` can be 0
if (!defined(savedSearchType) || !hasRecentSearches) {
return [];
}
// `savedSearchType` can be 0
if (!defined(savedSearchType) || !hasRecentSearches) {
return [];
}

const fetchFn = onGetRecentSearches || this.fetchRecentSearches;
return await fetchFn(this.state.query);
},
DEFAULT_DEBOUNCE_DURATION,
{leading: true}
);
const fetchFn = onGetRecentSearches || this.fetchRecentSearches;
return await fetchFn(this.state.query);
};

fetchRecentSearches = async (fullQuery: string): Promise<SearchItem[]> => {
const {api, organization, savedSearchType} = this.props;
Expand Down Expand Up @@ -1346,28 +1339,24 @@ class SmartSearchBar extends Component<DefaultProps & Props, State> {
}
};

getReleases = debounce(
async (tag: Tag, query: string) => {
const releasePromise = this.fetchReleases(query);
getReleases = async (tag: Tag, query: string) => {
const releasePromise = this.fetchReleases(query);

const tags = this.getPredefinedTagValues(tag, query);
const tagValues = tags.map<SearchItem>(v => ({
...v,
type: ItemType.FIRST_RELEASE,
}));
const tags = this.getPredefinedTagValues(tag, query);
const tagValues = tags.map<SearchItem>(v => ({
...v,
type: ItemType.FIRST_RELEASE,
}));

const releases = await releasePromise;
const releaseValues = releases.map<SearchItem>((r: any) => ({
value: r.shortVersion,
desc: r.shortVersion,
type: ItemType.FIRST_RELEASE,
}));
const releases = await releasePromise;
const releaseValues = releases.map<SearchItem>((r: any) => ({
value: r.shortVersion,
desc: r.shortVersion,
type: ItemType.FIRST_RELEASE,
}));

return [...tagValues, ...releaseValues];
},
DEFAULT_DEBOUNCE_DURATION,
{leading: true}
);
return [...tagValues, ...releaseValues];
};

/**
* Fetches latest releases from a organization/project. Returns an empty array
Expand Down Expand Up @@ -1622,9 +1611,13 @@ class SmartSearchBar extends Component<DefaultProps & Props, State> {
}
};

updateAutoCompleteItems = () => {
this.updateAutoCompleteFromAst();
};
updateAutoCompleteItems = debounce(
() => {
this.updateAutoCompleteFromAst();
},
DEFAULT_DEBOUNCE_DURATION,
{leading: true}
);

/**
* Updates autocomplete dropdown items and autocomplete index state
Expand Down
2 changes: 1 addition & 1 deletion static/app/views/issueList/savedIssueSearches.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ describe('SavedIssueSearches', function () {
beforeEach(() => {
localStorageWrapper.setItem(SAVED_SEARCHES_SIDEBAR_OPEN_LOCALSTORAGE_KEY, 'true');
MockApiClient.clearMockResponses();
jest.restoreAllMocks();
jest.clearAllMocks();
});

it('displays saved searches with correct text and in correct sections', async function () {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ describe('Performance > Web Vitals', function () {
});

afterEach(() => {
jest.restoreAllMocks();
jest.clearAllMocks();
});

it('render no access without feature', function () {
Expand Down

0 comments on commit caba48c

Please sign in to comment.