searchBar.tsx 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. import {useCallback} from 'react';
  2. // eslint-disable-next-line no-restricted-imports
  3. import {fetchTagValues} from 'sentry/actionCreators/tags';
  4. import SmartSearchBar from 'sentry/components/smartSearchBar';
  5. import {Organization, SavedSearchType, Tag, TagCollection} from 'sentry/types';
  6. import {getUtcDateString} from 'sentry/utils/dates';
  7. import {
  8. DEVICE_CLASS_TAG_VALUES,
  9. FieldKind,
  10. getFieldDefinition,
  11. isDeviceClass,
  12. } from 'sentry/utils/fields';
  13. import useApi from 'sentry/utils/useApi';
  14. import usePageFilters from 'sentry/utils/usePageFilters';
  15. import withIssueTags, {WithIssueTagsProps} from 'sentry/utils/withIssueTags';
  16. const getSupportedTags = (supportedTags: TagCollection) =>
  17. Object.fromEntries(
  18. Object.keys(supportedTags).map(key => [
  19. key,
  20. {
  21. ...supportedTags[key],
  22. kind:
  23. getFieldDefinition(key)?.kind ??
  24. (supportedTags[key].predefined ? FieldKind.FIELD : FieldKind.TAG),
  25. },
  26. ])
  27. );
  28. interface Props extends React.ComponentProps<typeof SmartSearchBar>, WithIssueTagsProps {
  29. organization: Organization;
  30. }
  31. const EXCLUDED_TAGS = ['environment'];
  32. function IssueListSearchBar({organization, tags, ...props}: Props) {
  33. const api = useApi();
  34. const {selection: pageFilters} = usePageFilters();
  35. const tagValueLoader = useCallback(
  36. (key: string, search: string) => {
  37. const orgSlug = organization.slug;
  38. const projectIds = pageFilters.projects.map(id => id.toString());
  39. const endpointParams = {
  40. start: getUtcDateString(pageFilters.datetime.start),
  41. end: getUtcDateString(pageFilters.datetime.end),
  42. statsPeriod: pageFilters.datetime.period,
  43. };
  44. return fetchTagValues({
  45. api,
  46. orgSlug,
  47. tagKey: key,
  48. search,
  49. projectIds,
  50. endpointParams,
  51. });
  52. },
  53. [
  54. api,
  55. organization.slug,
  56. pageFilters.datetime.end,
  57. pageFilters.datetime.period,
  58. pageFilters.datetime.start,
  59. pageFilters.projects,
  60. ]
  61. );
  62. const getTagValues = useCallback(
  63. async (tag: Tag, query: string): Promise<string[]> => {
  64. // device.class is stored as "numbers" in snuba, but we want to suggest high, medium,
  65. // and low search filter values because discover maps device.class to these values.
  66. if (isDeviceClass(tag.key)) {
  67. return DEVICE_CLASS_TAG_VALUES;
  68. }
  69. const values = await tagValueLoader(tag.key, query);
  70. return values.map(({value}) => value);
  71. },
  72. [tagValueLoader]
  73. );
  74. return (
  75. <SmartSearchBar
  76. hasRecentSearches
  77. savedSearchType={SavedSearchType.ISSUE}
  78. onGetTagValues={getTagValues}
  79. excludedTags={EXCLUDED_TAGS}
  80. maxMenuHeight={500}
  81. supportedTags={getSupportedTags(tags)}
  82. organization={organization}
  83. {...props}
  84. />
  85. );
  86. }
  87. export default withIssueTags(IssueListSearchBar);