widgetDetails.tsx 4.7 KB

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