content.tsx 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. import type {Theme} from '@emotion/react';
  2. import type {Location} from 'history';
  3. import ErrorPanel from 'sentry/components/charts/errorPanel';
  4. import LoadingPanel from 'sentry/components/charts/loadingPanel';
  5. import {IconWarning} from 'sentry/icons';
  6. import type {OrganizationSummary} from 'sentry/types/organization';
  7. import {defined} from 'sentry/utils';
  8. import EventView from 'sentry/utils/discover/eventView';
  9. import {useApiQuery} from 'sentry/utils/queryClient';
  10. import type {ViewProps} from '../../../types';
  11. import {filterToColor, SpanOperationBreakdownFilter} from '../../filter';
  12. import Chart from './chart';
  13. import {transformData} from './utils';
  14. type ApiResult = Record<string, number>;
  15. interface Props extends ViewProps {
  16. currentFilter: SpanOperationBreakdownFilter;
  17. fields: string[];
  18. location: Location;
  19. organization: OrganizationSummary;
  20. queryExtras?: Record<string, string>;
  21. }
  22. /**
  23. * Fetch and render a bar chart that shows event volume
  24. * for each duration bucket. We always render 15 buckets of
  25. * equal widths based on the endpoints min + max durations.
  26. *
  27. * This graph visualizes how many transactions were recorded
  28. * at each duration bucket, showing the modality of the transaction.
  29. */
  30. function Content({
  31. currentFilter,
  32. fields,
  33. location,
  34. organization,
  35. queryExtras,
  36. start,
  37. end,
  38. query,
  39. statsPeriod,
  40. environment,
  41. project,
  42. }: Props) {
  43. const eventView = EventView.fromSavedQuery({
  44. id: '',
  45. name: '',
  46. version: 2,
  47. fields,
  48. orderby: '',
  49. projects: project,
  50. range: statsPeriod,
  51. query,
  52. environment,
  53. start,
  54. end,
  55. });
  56. let apiPayload = eventView.getEventsAPIPayload(location);
  57. apiPayload = {
  58. ...apiPayload,
  59. ...queryExtras,
  60. referrer: 'api.performance.durationpercentilechart',
  61. };
  62. const {
  63. data: chartData,
  64. isPending,
  65. isError,
  66. } = useApiQuery<{data: ApiResult[]}>(
  67. [`/organizations/${organization.slug}/events/`, {query: apiPayload}],
  68. {
  69. staleTime: 0,
  70. }
  71. );
  72. if (isError) {
  73. return (
  74. <ErrorPanel>
  75. <IconWarning color="gray300" size="lg" />
  76. </ErrorPanel>
  77. );
  78. }
  79. if (isPending) {
  80. return <LoadingPanel data-test-id="histogram-loading" />;
  81. }
  82. if (!defined(chartData)) {
  83. return null;
  84. }
  85. const colors = (theme: Theme) =>
  86. currentFilter === SpanOperationBreakdownFilter.NONE
  87. ? theme.charts.getColorPalette(1)
  88. : [filterToColor(currentFilter)];
  89. return <Chart series={transformData(chartData.data, false)} colors={colors} />;
  90. }
  91. export default Content;