widgetDetails.tsx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  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. // TODO: add types
  63. export function MetricDetails({
  64. mri,
  65. query,
  66. focusedSeries,
  67. onRowHover,
  68. focusArea,
  69. }: MetricDetailsProps) {
  70. const organization = useOrganization();
  71. const [selectedTab, setSelectedTab] = useState(Tab.SAMPLES);
  72. const isCodeLocationsDisabled = mri && !isCustomMetric({mri});
  73. if (isCodeLocationsDisabled && selectedTab === Tab.CODE_LOCATIONS) {
  74. setSelectedTab(Tab.SAMPLES);
  75. }
  76. const queryWithFocusedSeries = useMemo(
  77. () => focusedSeries && getQueryWithFocusedSeries(query || '', focusedSeries),
  78. [focusedSeries, query]
  79. );
  80. const handleTabChange = useCallback(
  81. (tab: Tab) => {
  82. if (tab === Tab.CODE_LOCATIONS) {
  83. trackAnalytics('ddm.code-locations', {
  84. organization,
  85. });
  86. }
  87. setSelectedTab(tab);
  88. },
  89. [organization]
  90. );
  91. return (
  92. <TrayWrapper>
  93. <Tabs value={selectedTab} onChange={handleTabChange}>
  94. <TabList>
  95. <TabList.Item key={Tab.SAMPLES}>{t('Sampled Events')}</TabList.Item>
  96. <TabList.Item
  97. textValue={t('Code Location')}
  98. key={Tab.CODE_LOCATIONS}
  99. disabled={isCodeLocationsDisabled}
  100. >
  101. <Tooltip
  102. title={t(
  103. 'This metric is automatically collected by Sentry. It is not bound to a specific line of your code.'
  104. )}
  105. disabled={!isCodeLocationsDisabled}
  106. >
  107. <span style={{pointerEvents: 'all'}}>{t('Code Location')}</span>
  108. </Tooltip>
  109. </TabList.Item>
  110. </TabList>
  111. <ContentWrapper>
  112. <TabPanels>
  113. <TabPanels.Item key={Tab.SAMPLES}>
  114. {organization.features.includes('metrics-samples-list') ? (
  115. <MetricSamplesTable
  116. focusArea={focusArea?.selection?.range}
  117. mri={mri}
  118. query={queryWithFocusedSeries}
  119. />
  120. ) : (
  121. <SampleTable
  122. mri={mri}
  123. {...focusArea?.selection?.range}
  124. query={queryWithFocusedSeries}
  125. onRowHover={onRowHover}
  126. />
  127. )}
  128. </TabPanels.Item>
  129. <TabPanels.Item key={Tab.CODE_LOCATIONS}>
  130. <CodeLocations mri={mri} {...focusArea?.selection?.range} />
  131. </TabPanels.Item>
  132. </TabPanels>
  133. </ContentWrapper>
  134. </Tabs>
  135. </TrayWrapper>
  136. );
  137. }
  138. const TrayWrapper = styled('div')`
  139. padding-top: ${space(4)};
  140. display: grid;
  141. grid-template-rows: auto auto 1fr;
  142. `;
  143. const ContentWrapper = styled('div')`
  144. position: relative;
  145. padding-top: ${space(2)};
  146. `;