metricFormulaContextMenu.tsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. import {useMemo} from 'react';
  2. import styled from '@emotion/styled';
  3. import Feature from 'sentry/components/acl/feature';
  4. import type {MenuItemProps} from 'sentry/components/dropdownMenu';
  5. import {DropdownMenu} from 'sentry/components/dropdownMenu';
  6. import {IconClose, IconCopy, IconDashboard, IconEllipsis} from 'sentry/icons';
  7. import {t} from 'sentry/locale';
  8. import {trackAnalytics} from 'sentry/utils/analytics';
  9. import {isCustomMeasurement} from 'sentry/utils/metrics';
  10. import {hasCustomMetrics} from 'sentry/utils/metrics/features';
  11. import type {MetricsEquationWidget} from 'sentry/utils/metrics/types';
  12. import useOrganization from 'sentry/utils/useOrganization';
  13. import {useMetricsContext} from 'sentry/views/metrics/context';
  14. import {useCreateDashboard} from 'sentry/views/metrics/useCreateDashboard';
  15. import type {useFormulaDependencies} from 'sentry/views/metrics/utils/useFormulaDependencies';
  16. type ContextMenuProps = {
  17. formulaDependencies: ReturnType<typeof useFormulaDependencies>;
  18. formulaWidget: MetricsEquationWidget;
  19. widgetIndex: number;
  20. };
  21. export function MetricFormulaContextMenu({
  22. widgetIndex,
  23. formulaWidget,
  24. formulaDependencies,
  25. }: ContextMenuProps) {
  26. const organization = useOrganization();
  27. const {removeWidget, duplicateWidget, widgets} = useMetricsContext();
  28. const canDelete = widgets.length > 1;
  29. const createDashboardWidget = useCreateDashboardWidget(
  30. formulaWidget,
  31. formulaDependencies
  32. );
  33. const items = useMemo<MenuItemProps[]>(
  34. () => [
  35. {
  36. leadingItems: [<IconCopy key="icon" />],
  37. key: 'duplicate',
  38. label: t('Duplicate'),
  39. onAction: () => {
  40. trackAnalytics('ddm.widget.duplicate', {
  41. organization,
  42. });
  43. duplicateWidget(widgetIndex);
  44. },
  45. },
  46. {
  47. leadingItems: [<IconDashboard key="icon" />],
  48. key: 'add-dashboard',
  49. label: (
  50. <Feature
  51. organization={organization}
  52. hookName="feature-disabled:dashboards-edit"
  53. features="dashboards-edit"
  54. >
  55. {({hasFeature}) => (
  56. <AddToDashboardItem disabled={!hasFeature}>
  57. {t('Add to Dashboard')}
  58. </AddToDashboardItem>
  59. )}
  60. </Feature>
  61. ),
  62. disabled: !createDashboardWidget,
  63. onAction: () => {
  64. if (!organization.features.includes('dashboards-edit')) {
  65. return;
  66. }
  67. trackAnalytics('ddm.add-to-dashboard', {
  68. organization,
  69. source: 'widget',
  70. });
  71. createDashboardWidget?.();
  72. },
  73. },
  74. {
  75. leadingItems: [<IconClose key="icon" />],
  76. key: 'delete',
  77. label: t('Remove Equation'),
  78. disabled: !canDelete,
  79. onAction: () => {
  80. removeWidget(widgetIndex);
  81. },
  82. },
  83. ],
  84. [
  85. organization,
  86. createDashboardWidget,
  87. canDelete,
  88. duplicateWidget,
  89. widgetIndex,
  90. removeWidget,
  91. ]
  92. );
  93. if (!hasCustomMetrics(organization)) {
  94. return null;
  95. }
  96. return (
  97. <DropdownMenu
  98. items={items}
  99. triggerProps={{
  100. 'aria-label': t('Widget actions'),
  101. size: 'md',
  102. showChevron: false,
  103. icon: <IconEllipsis direction="down" size="sm" />,
  104. }}
  105. position="bottom-end"
  106. />
  107. );
  108. }
  109. function useCreateDashboardWidget(
  110. formulaWidget: MetricsEquationWidget,
  111. formulaDependencies: ReturnType<typeof useFormulaDependencies>
  112. ) {
  113. const {dependencies, isError} = formulaDependencies[formulaWidget.id]!;
  114. const widgetArray = useMemo(() => [formulaWidget], [formulaWidget]);
  115. const createDashboard = useCreateDashboard(widgetArray, formulaDependencies, false);
  116. if (!formulaWidget.formula || isError || dependencies.some(isCustomMeasurement)) {
  117. return undefined;
  118. }
  119. return createDashboard;
  120. }
  121. const AddToDashboardItem = styled('div')<{disabled: boolean}>`
  122. color: ${p => (p.disabled ? p.theme.disabled : p.theme.textColor)};
  123. `;