contextMenu.tsx 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. import {useMemo} from 'react';
  2. import styled from '@emotion/styled';
  3. import {openAddToDashboardModal, openModal} from 'sentry/actionCreators/modal';
  4. import {DropdownMenu, MenuItemProps} from 'sentry/components/dropdownMenu';
  5. import {IconCopy, IconDashboard, IconDelete, IconEllipsis, IconSiren} from 'sentry/icons';
  6. import {t} from 'sentry/locale';
  7. import {space} from 'sentry/styles/space';
  8. import {Organization} from 'sentry/types';
  9. import {isCustomMeasurement, MetricDisplayType, MetricsQuery} from 'sentry/utils/metrics';
  10. import {
  11. convertToDashboardWidget,
  12. encodeWidgetQuery,
  13. getWidgetAsQueryParams,
  14. getWidgetQuery,
  15. } from 'sentry/utils/metrics/dashboard';
  16. import {hasDDMFeature} from 'sentry/utils/metrics/features';
  17. import useOrganization from 'sentry/utils/useOrganization';
  18. import useRouter from 'sentry/utils/useRouter';
  19. import {useDDMContext} from 'sentry/views/ddm/context';
  20. import {CreateAlertModal} from 'sentry/views/ddm/createAlertModal';
  21. import {OrganizationContext} from 'sentry/views/organizationContext';
  22. type ContextMenuProps = {
  23. displayType: MetricDisplayType;
  24. metricsQuery: MetricsQuery;
  25. widgetIndex: number;
  26. };
  27. export function MetricWidgetContextMenu({
  28. metricsQuery,
  29. displayType,
  30. widgetIndex,
  31. }: ContextMenuProps) {
  32. const organization = useOrganization();
  33. const {removeWidget, duplicateWidget, widgets} = useDDMContext();
  34. const createAlert = useCreateAlert(organization, metricsQuery);
  35. const createDashboardWidget = useCreateDashboardWidget(
  36. organization,
  37. metricsQuery,
  38. displayType
  39. );
  40. const canDelete = widgets.length > 1;
  41. const items = useMemo<MenuItemProps[]>(
  42. () => [
  43. {
  44. leadingItems: [<IconCopy key="icon" />],
  45. key: 'duplicate',
  46. label: t('Duplicate'),
  47. onAction: () => duplicateWidget(widgetIndex),
  48. },
  49. {
  50. leadingItems: [<IconSiren key="icon" />],
  51. key: 'add-alert',
  52. label: t('Create Alert'),
  53. disabled: !createAlert,
  54. onAction: createAlert,
  55. },
  56. {
  57. leadingItems: [<IconDashboard key="icon" />],
  58. key: 'add-dashoard',
  59. label: t('Add to Dashboard'),
  60. disabled: !createDashboardWidget,
  61. onAction: createDashboardWidget,
  62. },
  63. {
  64. leadingItems: [<IconDelete key="icon" />],
  65. key: 'delete',
  66. label: t('Delete'),
  67. disabled: !canDelete,
  68. onAction: () => removeWidget(widgetIndex),
  69. },
  70. ],
  71. [
  72. createAlert,
  73. createDashboardWidget,
  74. duplicateWidget,
  75. removeWidget,
  76. widgetIndex,
  77. canDelete,
  78. ]
  79. );
  80. if (!hasDDMFeature(organization)) {
  81. return null;
  82. }
  83. return (
  84. <StyledDropdownMenuControl
  85. items={items}
  86. triggerProps={{
  87. 'aria-label': t('Widget actions'),
  88. size: 'xs',
  89. borderless: true,
  90. showChevron: false,
  91. icon: <IconEllipsis direction="down" size="sm" />,
  92. }}
  93. position="bottom-end"
  94. />
  95. );
  96. }
  97. const StyledDropdownMenuControl = styled(DropdownMenu)`
  98. margin: ${space(1)};
  99. `;
  100. export function useCreateAlert(organization: Organization, metricsQuery: MetricsQuery) {
  101. return useMemo(() => {
  102. if (
  103. !metricsQuery.mri ||
  104. !metricsQuery.op ||
  105. isCustomMeasurement(metricsQuery) ||
  106. !organization.access.includes('alerts:write')
  107. ) {
  108. return undefined;
  109. }
  110. return function () {
  111. return openModal(deps => (
  112. <OrganizationContext.Provider value={organization}>
  113. <CreateAlertModal metricsQuery={metricsQuery} {...deps} />
  114. </OrganizationContext.Provider>
  115. ));
  116. };
  117. }, [metricsQuery, organization]);
  118. }
  119. export function useCreateDashboardWidget(
  120. organization: Organization,
  121. metricsQuery: MetricsQuery,
  122. displayType?: MetricDisplayType
  123. ) {
  124. const router = useRouter();
  125. const {projects, environments, datetime} = metricsQuery;
  126. return useMemo(() => {
  127. if (!metricsQuery.mri || !metricsQuery.op || isCustomMeasurement(metricsQuery)) {
  128. return undefined;
  129. }
  130. const widgetQuery = getWidgetQuery(metricsQuery);
  131. const urlWidgetQuery = encodeWidgetQuery(widgetQuery);
  132. const widgetAsQueryParams = getWidgetAsQueryParams(
  133. metricsQuery,
  134. urlWidgetQuery,
  135. displayType
  136. );
  137. return () =>
  138. openAddToDashboardModal({
  139. organization,
  140. selection: {
  141. projects,
  142. environments,
  143. datetime,
  144. },
  145. widget: convertToDashboardWidget(metricsQuery, displayType),
  146. router,
  147. widgetAsQueryParams,
  148. location: router.location,
  149. });
  150. }, [metricsQuery, datetime, displayType, environments, organization, projects, router]);
  151. }