index.tsx 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. import {Fragment, useMemo} from 'react';
  2. import styled from '@emotion/styled';
  3. import {getInterval} from 'sentry/components/charts/utils';
  4. import {CompactSelect} from 'sentry/components/compactSelect';
  5. import {CHART_PALETTE} from 'sentry/constants/chartPalette';
  6. import {t} from 'sentry/locale';
  7. import {space} from 'sentry/styles/space';
  8. import {dedupeArray} from 'sentry/utils/dedupeArray';
  9. import {aggregateOutputType} from 'sentry/utils/discover/fields';
  10. import {MutableSearch} from 'sentry/utils/tokenizeSearch';
  11. import usePageFilters from 'sentry/utils/usePageFilters';
  12. import {useChartInterval} from 'sentry/views/explore/hooks/useChartInterval';
  13. import {useChartType} from 'sentry/views/explore/hooks/useChartType';
  14. import {useDataset} from 'sentry/views/explore/hooks/useDataset';
  15. import {useVisualizes} from 'sentry/views/explore/hooks/useVisualizes';
  16. import Chart, {ChartType} from 'sentry/views/insights/common/components/chart';
  17. import ChartPanel from 'sentry/views/insights/common/components/chartPanel';
  18. import {useSpanIndexedSeries} from 'sentry/views/insights/common/queries/useDiscoverSeries';
  19. import {CHART_HEIGHT} from 'sentry/views/insights/database/settings';
  20. interface ExploreChartsProps {
  21. query: string;
  22. }
  23. const exploreChartTypeOptions = [
  24. {
  25. value: ChartType.LINE,
  26. label: t('Line'),
  27. },
  28. {
  29. value: ChartType.AREA,
  30. label: t('Area'),
  31. },
  32. {
  33. value: ChartType.BAR,
  34. label: t('Bar'),
  35. },
  36. ];
  37. // TODO: Update to support aggregate mode and multiple queries / visualizations
  38. export function ExploreCharts({query}: ExploreChartsProps) {
  39. const pageFilters = usePageFilters();
  40. const [dataset] = useDataset();
  41. const [visualizes] = useVisualizes();
  42. const [chartType, setChartType] = useChartType();
  43. const [interval, setInterval, intervalOptions] = useChartInterval();
  44. const yAxes = useMemo(() => {
  45. const deduped = dedupeArray(visualizes.flatMap(visualize => visualize.yAxes));
  46. deduped.sort();
  47. return deduped;
  48. }, [visualizes]);
  49. const series = useSpanIndexedSeries(
  50. {
  51. search: new MutableSearch(query ?? ''),
  52. yAxis: yAxes,
  53. interval: interval ?? getInterval(pageFilters.selection.datetime, 'metrics'),
  54. enabled: true,
  55. },
  56. 'api.explorer.stats',
  57. dataset
  58. );
  59. return (
  60. <Fragment>
  61. {visualizes.map((visualize, index) => {
  62. const dedupedYAxes = dedupeArray(visualize.yAxes);
  63. return (
  64. <ChartContainer key={index}>
  65. <ChartPanel>
  66. <ChartHeader>
  67. <ChartTitle>{dedupedYAxes.join(',')}</ChartTitle>
  68. <ChartSettingsContainer>
  69. <CompactSelect
  70. size="xs"
  71. triggerProps={{prefix: t('Display')}}
  72. value={chartType}
  73. options={exploreChartTypeOptions}
  74. onChange={newChartType => setChartType(newChartType.value)}
  75. />
  76. <CompactSelect
  77. size="xs"
  78. value={interval}
  79. onChange={({value}) => setInterval(value)}
  80. triggerProps={{
  81. prefix: t('Interval'),
  82. }}
  83. options={intervalOptions}
  84. />
  85. </ChartSettingsContainer>
  86. </ChartHeader>
  87. <Chart
  88. height={CHART_HEIGHT}
  89. grid={{
  90. left: '0',
  91. right: '0',
  92. top: '8px',
  93. bottom: '0',
  94. }}
  95. data={dedupedYAxes.map(yAxis => series.data[yAxis])}
  96. error={series.error}
  97. loading={series.isPending}
  98. chartColors={CHART_PALETTE[2]}
  99. type={chartType}
  100. // for now, use the first y axis unit
  101. aggregateOutputFormat={aggregateOutputType(dedupedYAxes[0])}
  102. showLegend
  103. />
  104. </ChartPanel>
  105. </ChartContainer>
  106. );
  107. })}
  108. </Fragment>
  109. );
  110. }
  111. const ChartContainer = styled('div')`
  112. display: grid;
  113. gap: 0;
  114. grid-template-columns: 1fr;
  115. margin-bottom: ${space(3)};
  116. `;
  117. const ChartHeader = styled('div')`
  118. display: flex;
  119. align-items: flex-start;
  120. justify-content: space-between;
  121. margin-bottom: ${space(1)};
  122. `;
  123. const ChartTitle = styled('div')`
  124. ${p => p.theme.text.cardTitle}
  125. `;
  126. const ChartSettingsContainer = styled('div')`
  127. display: flex;
  128. align-items: center;
  129. gap: ${space(0.5)};
  130. `;