actionDropdown.tsx 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. import {browserHistory} from 'react-router';
  2. import styled from '@emotion/styled';
  3. import {Location} from 'history';
  4. import {Button} from 'sentry/components/button';
  5. import {DropdownMenu, MenuItemProps} from 'sentry/components/dropdownMenu';
  6. import {IconEllipsis} from 'sentry/icons';
  7. import {t} from 'sentry/locale';
  8. import {space} from 'sentry/styles/space';
  9. import {Organization} from 'sentry/types';
  10. import {trackAnalytics} from 'sentry/utils/analytics';
  11. import EventView, {EventData} from 'sentry/utils/discover/eventView';
  12. import toArray from 'sentry/utils/toArray';
  13. import {MutableSearch} from 'sentry/utils/tokenizeSearch';
  14. import {addToFilter, excludeFromFilter} from '../cellAction';
  15. export enum ContextValueType {
  16. STRING = 'string',
  17. NUMBER = 'number',
  18. DURATION = 'duration',
  19. }
  20. enum QueryUpdateActions {
  21. ADD = 'add',
  22. EXCLUDE = 'exclude',
  23. SHOW_MORE_THAN = 'show-more-than',
  24. SHOW_LESS_THAN = 'show-less-than',
  25. }
  26. type Props = {
  27. contextValueType: ContextValueType;
  28. dataRow: EventData;
  29. eventView: EventView;
  30. location: Location;
  31. organization: Organization;
  32. queryKey: string;
  33. value: React.ReactText | string[];
  34. };
  35. function ActionDropDown(props: Props) {
  36. const menuItems: MenuItemProps[] = [];
  37. const {location, eventView, queryKey, value, organization, contextValueType, dataRow} =
  38. props;
  39. const addAsColumn = () => {
  40. trackAnalytics('discover_v2.quick_context_add_column', {
  41. organization,
  42. column: queryKey,
  43. });
  44. const oldField = eventView?.fields.map(field => field.field);
  45. const newField = toArray(oldField).concat(queryKey);
  46. browserHistory.push({
  47. ...location,
  48. query: {
  49. ...location?.query,
  50. field: newField,
  51. },
  52. });
  53. };
  54. function handleQueryUpdate(actionType: QueryUpdateActions) {
  55. trackAnalytics('discover_v2.quick_context_update_query', {
  56. organization,
  57. queryKey,
  58. });
  59. const oldFilters = eventView?.query || '';
  60. const newFilters = new MutableSearch(oldFilters);
  61. switch (actionType) {
  62. case QueryUpdateActions.SHOW_MORE_THAN:
  63. newFilters.setFilterValues(queryKey, [`>${value}`]);
  64. break;
  65. case QueryUpdateActions.SHOW_LESS_THAN:
  66. newFilters.setFilterValues(queryKey, [`<${value}`]);
  67. break;
  68. case QueryUpdateActions.ADD:
  69. addToFilter(newFilters, queryKey, value);
  70. break;
  71. case QueryUpdateActions.EXCLUDE:
  72. excludeFromFilter(newFilters, queryKey, value);
  73. break;
  74. default:
  75. throw new Error(`Unknown quick context action type. ${actionType}`);
  76. }
  77. browserHistory.push({
  78. ...location,
  79. query: {
  80. ...location?.query,
  81. query: newFilters.formatString(),
  82. },
  83. });
  84. }
  85. if (!(queryKey in dataRow)) {
  86. menuItems.push({
  87. key: 'add-as-column',
  88. label: t('Add as column'),
  89. onAction: () => {
  90. addAsColumn();
  91. },
  92. });
  93. }
  94. if (
  95. contextValueType === ContextValueType.NUMBER ||
  96. contextValueType === ContextValueType.DURATION
  97. ) {
  98. menuItems.push(
  99. {
  100. key: 'show-more-than',
  101. label: t('Show values greater than'),
  102. onAction: () => {
  103. handleQueryUpdate(QueryUpdateActions.SHOW_MORE_THAN);
  104. },
  105. },
  106. {
  107. key: 'show-less-than',
  108. label: t('Show values less than'),
  109. onAction: () => {
  110. handleQueryUpdate(QueryUpdateActions.SHOW_LESS_THAN);
  111. },
  112. }
  113. );
  114. } else {
  115. menuItems.push(
  116. {
  117. key: 'add-to-filter',
  118. label: t('Add to filter'),
  119. onAction: () => {
  120. handleQueryUpdate(QueryUpdateActions.ADD);
  121. },
  122. },
  123. {
  124. key: 'exclude-from-filter',
  125. label: t('Exclude from filter'),
  126. onAction: () => {
  127. handleQueryUpdate(QueryUpdateActions.EXCLUDE);
  128. },
  129. }
  130. );
  131. }
  132. return (
  133. <DropdownMenu
  134. items={menuItems}
  135. trigger={triggerProps => (
  136. <StyledTrigger
  137. {...triggerProps}
  138. aria-label={t('Quick Context Action Menu')}
  139. data-test-id="quick-context-action-trigger"
  140. borderless
  141. size="zero"
  142. onClick={e => {
  143. e.stopPropagation();
  144. e.preventDefault();
  145. triggerProps.onClick?.(e);
  146. }}
  147. icon={<IconEllipsis size="sm" />}
  148. />
  149. )}
  150. />
  151. );
  152. }
  153. const StyledTrigger = styled(Button)`
  154. margin-left: ${space(0.5)};
  155. `;
  156. export default ActionDropDown;