widgetDetails.tsx 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. import {useCallback, useMemo, useState} from 'react';
  2. import styled from '@emotion/styled';
  3. import {MetricSamplesTable} from 'sentry/components/ddm/metricSamplesTable';
  4. import {TabList, TabPanels, Tabs} from 'sentry/components/tabs';
  5. import {Tooltip} from 'sentry/components/tooltip';
  6. import {t} from 'sentry/locale';
  7. import {space} from 'sentry/styles/space';
  8. import {trackAnalytics} from 'sentry/utils/analytics';
  9. import {isCustomMetric} from 'sentry/utils/metrics';
  10. import {
  11. MetricQueryType,
  12. type MetricQueryWidgetParams,
  13. type MetricWidgetQueryParams,
  14. } from 'sentry/utils/metrics/types';
  15. import useOrganization from 'sentry/utils/useOrganization';
  16. import {CodeLocations} from 'sentry/views/ddm/codeLocations';
  17. import type {FocusAreaProps} from 'sentry/views/ddm/context';
  18. import {useDDMContext} from 'sentry/views/ddm/context';
  19. import type {SamplesTableProps} from 'sentry/views/ddm/sampleTable';
  20. import {SampleTable} from 'sentry/views/ddm/sampleTable';
  21. import {getQueryWithFocusedSeries} from 'sentry/views/ddm/utils';
  22. enum Tab {
  23. SAMPLES = 'samples',
  24. CODE_LOCATIONS = 'codeLocations',
  25. }
  26. export function WidgetDetails() {
  27. const {selectedWidgetIndex, widgets, focusArea, setHighlightedSampleId} =
  28. useDDMContext();
  29. const selectedWidget = widgets[selectedWidgetIndex] as
  30. | MetricWidgetQueryParams
  31. | undefined;
  32. const handleSampleRowHover = useCallback(
  33. (sampleId?: string) => {
  34. setHighlightedSampleId(sampleId);
  35. },
  36. [setHighlightedSampleId]
  37. );
  38. return (
  39. <MetricDetails
  40. // TODO(aknaus): better fallback
  41. widget={
  42. selectedWidget?.type === MetricQueryType.FORMULA ? undefined : selectedWidget
  43. }
  44. onRowHover={handleSampleRowHover}
  45. focusArea={focusArea}
  46. />
  47. );
  48. }
  49. interface MetricDetailsProps {
  50. focusArea?: FocusAreaProps;
  51. onRowHover?: SamplesTableProps['onRowHover'];
  52. widget?: MetricQueryWidgetParams;
  53. }
  54. export function MetricDetails({widget, onRowHover, focusArea}: MetricDetailsProps) {
  55. const organization = useOrganization();
  56. const [selectedTab, setSelectedTab] = useState(Tab.SAMPLES);
  57. const isCodeLocationsDisabled = widget?.mri && !isCustomMetric({mri: widget.mri});
  58. if (isCodeLocationsDisabled && selectedTab === Tab.CODE_LOCATIONS) {
  59. setSelectedTab(Tab.SAMPLES);
  60. }
  61. const queryWithFocusedSeries = useMemo(
  62. () => widget && getQueryWithFocusedSeries(widget.query || '', widget.focusedSeries),
  63. [widget]
  64. );
  65. const handleTabChange = useCallback(
  66. (tab: Tab) => {
  67. if (tab === Tab.CODE_LOCATIONS) {
  68. trackAnalytics('ddm.code-locations', {
  69. organization,
  70. });
  71. }
  72. setSelectedTab(tab);
  73. },
  74. [organization]
  75. );
  76. return (
  77. <TrayWrapper>
  78. <Tabs value={selectedTab} onChange={handleTabChange}>
  79. <TabList>
  80. <TabList.Item key={Tab.SAMPLES}>{t('Sampled Events')}</TabList.Item>
  81. <TabList.Item
  82. textValue={t('Code Location')}
  83. key={Tab.CODE_LOCATIONS}
  84. disabled={isCodeLocationsDisabled}
  85. >
  86. <Tooltip
  87. title={t(
  88. 'This metric is automatically collected by Sentry. It is not bound to a specific line of your code.'
  89. )}
  90. disabled={!isCodeLocationsDisabled}
  91. >
  92. <span style={{pointerEvents: 'all'}}>{t('Code Location')}</span>
  93. </Tooltip>
  94. </TabList.Item>
  95. </TabList>
  96. <ContentWrapper>
  97. <TabPanels>
  98. <TabPanels.Item key={Tab.SAMPLES}>
  99. {organization.features.includes('metrics-samples-list') ? (
  100. <MetricSamplesTable mri={widget?.mri} query={queryWithFocusedSeries} />
  101. ) : (
  102. <SampleTable
  103. mri={widget?.mri}
  104. {...focusArea?.selection?.range}
  105. query={queryWithFocusedSeries}
  106. onRowHover={onRowHover}
  107. />
  108. )}
  109. </TabPanels.Item>
  110. <TabPanels.Item key={Tab.CODE_LOCATIONS}>
  111. <CodeLocations mri={widget?.mri} {...focusArea?.selection?.range} />
  112. </TabPanels.Item>
  113. </TabPanels>
  114. </ContentWrapper>
  115. </Tabs>
  116. </TrayWrapper>
  117. );
  118. }
  119. const TrayWrapper = styled('div')`
  120. padding-top: ${space(4)};
  121. display: grid;
  122. grid-template-rows: auto auto 1fr;
  123. `;
  124. const ContentWrapper = styled('div')`
  125. position: relative;
  126. padding-top: ${space(2)};
  127. `;