chartFooter.tsx 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. import {Fragment} from 'react';
  2. import styled from '@emotion/styled';
  3. import Feature from 'sentry/components/acl/feature';
  4. import IntervalSelector from 'sentry/components/charts/intervalSelector';
  5. import OptionSelector from 'sentry/components/charts/optionSelector';
  6. import {
  7. ChartControls,
  8. InlineContainer,
  9. SectionHeading,
  10. SectionValue,
  11. } from 'sentry/components/charts/styles';
  12. import FeatureBadge from 'sentry/components/featureBadge';
  13. import ExternalLink from 'sentry/components/links/externalLink';
  14. import QuestionTooltip from 'sentry/components/questionTooltip';
  15. import Switch from 'sentry/components/switchButton';
  16. import {t, tct} from 'sentry/locale';
  17. import {Organization, SelectValue} from 'sentry/types';
  18. import {defined} from 'sentry/utils';
  19. import trackAdvancedAnalyticsEvent from 'sentry/utils/analytics/trackAdvancedAnalyticsEvent';
  20. import EventView from 'sentry/utils/discover/eventView';
  21. import {TOP_EVENT_MODES} from 'sentry/utils/discover/types';
  22. import {formatAbbreviatedNumber} from 'sentry/utils/formatters';
  23. import localStorage from 'sentry/utils/localStorage';
  24. export const PROCESSED_BASELINE_TOGGLE_KEY = 'show-processed-baseline';
  25. type Props = {
  26. displayMode: string;
  27. displayOptions: SelectValue<string>[];
  28. eventView: EventView;
  29. onAxisChange: (value: string[]) => void;
  30. onDisplayChange: (value: string) => void;
  31. onIntervalChange: (value: string | undefined) => void;
  32. onTopEventsChange: (value: string) => void;
  33. organization: Organization;
  34. setShowBaseline: (value: boolean) => void;
  35. showBaseline: boolean;
  36. topEvents: string;
  37. total: number | null;
  38. yAxisOptions: SelectValue<string>[];
  39. yAxisValue: string[];
  40. disableProcessedBaselineToggle?: boolean;
  41. loadingProcessedTotals?: boolean;
  42. processedTotal?: number;
  43. };
  44. export default function ChartFooter({
  45. total,
  46. yAxisValue,
  47. yAxisOptions,
  48. onAxisChange,
  49. displayMode,
  50. displayOptions,
  51. onDisplayChange,
  52. onTopEventsChange,
  53. onIntervalChange,
  54. topEvents,
  55. setShowBaseline,
  56. showBaseline,
  57. organization,
  58. disableProcessedBaselineToggle,
  59. eventView,
  60. processedTotal,
  61. loadingProcessedTotals,
  62. }: Props) {
  63. const elements: React.ReactNode[] = [];
  64. elements.push(<SectionHeading key="total-label">{t('Total Events')}</SectionHeading>);
  65. elements.push(
  66. total === null || loadingProcessedTotals === true ? (
  67. <SectionValue data-test-id="loading-placeholder" key="total-value">
  68. &mdash;
  69. </SectionValue>
  70. ) : defined(processedTotal) ? (
  71. <SectionValue key="total-value">
  72. {tct('[indexedTotal] of [processedTotal]', {
  73. indexedTotal: formatAbbreviatedNumber(total),
  74. processedTotal: formatAbbreviatedNumber(processedTotal),
  75. })}
  76. </SectionValue>
  77. ) : (
  78. <SectionValue key="total-value">{total.toLocaleString()}</SectionValue>
  79. )
  80. );
  81. const topEventOptions: SelectValue<string>[] = [];
  82. for (let i = 1; i <= 10; i++) {
  83. topEventOptions.push({value: i.toString(), label: i.toString()});
  84. }
  85. return (
  86. <ChartControls>
  87. <InlineContainer>{elements}</InlineContainer>
  88. <InlineContainer>
  89. <Feature organization={organization} features={['discover-metrics-baseline']}>
  90. <Fragment>
  91. <SwitchLabel>{t('Processed events')}</SwitchLabel>
  92. <Switch
  93. data-test-id="processed-events-toggle"
  94. isActive={showBaseline}
  95. isDisabled={disableProcessedBaselineToggle ?? true}
  96. size="lg"
  97. toggle={() => {
  98. const value = !showBaseline;
  99. localStorage.setItem(
  100. PROCESSED_BASELINE_TOGGLE_KEY,
  101. value === true ? '1' : '0'
  102. );
  103. trackAdvancedAnalyticsEvent(
  104. 'discover_v2.processed_baseline_toggle.clicked',
  105. {
  106. organization,
  107. toggled: value === true ? 'on' : 'off',
  108. }
  109. );
  110. setShowBaseline(value);
  111. }}
  112. />
  113. <QuestionTooltip
  114. isHoverable
  115. position="top"
  116. size="sm"
  117. title={tct(
  118. 'Show a baseline of client-side [processedEventsLink: processed events].[break]Available on the Total Period display for y-axes scoped to [transactionEventsLink: transaction events].',
  119. {
  120. transactionEventsLink: (
  121. <ExternalLink href="https://docs.sentry.io/product/sentry-basics/tracing/event-detail/" />
  122. ),
  123. processedEventsLink: (
  124. <ExternalLink href="https://docs.sentry.io/product/data-management-settings/server-side-sampling/" />
  125. ),
  126. break: (
  127. <div>
  128. <br />
  129. </div>
  130. ),
  131. }
  132. )}
  133. />
  134. <FeatureBadge type="beta" />
  135. </Fragment>
  136. </Feature>
  137. <IntervalSelector
  138. displayMode={displayMode}
  139. eventView={eventView}
  140. onIntervalChange={onIntervalChange}
  141. />
  142. <OptionSelector
  143. title={t('Display')}
  144. selected={displayMode}
  145. options={displayOptions}
  146. onChange={onDisplayChange}
  147. />
  148. {TOP_EVENT_MODES.includes(displayMode) && (
  149. <OptionSelector
  150. title={t('Limit')}
  151. selected={topEvents}
  152. options={topEventOptions}
  153. onChange={onTopEventsChange}
  154. />
  155. )}
  156. {TOP_EVENT_MODES.includes(displayMode) ? (
  157. <OptionSelector
  158. title={t('Y-Axis')}
  159. selected={yAxisValue[0]}
  160. options={yAxisOptions}
  161. onChange={yAxis => onAxisChange([yAxis])}
  162. />
  163. ) : (
  164. <OptionSelector
  165. multiple
  166. isClearable
  167. menuTitle={
  168. yAxisOptions.length > 3 ? t('Select up to 3 options') : t('Y-axis')
  169. }
  170. title={t('Y-Axis')}
  171. selected={yAxisValue}
  172. options={yAxisOptions}
  173. onChange={onAxisChange}
  174. />
  175. )}
  176. </InlineContainer>
  177. </ChartControls>
  178. );
  179. }
  180. const SwitchLabel = styled('div')`
  181. padding-right: 4px;
  182. font-weight: bold;
  183. `;