screenBarChart.tsx 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. import styled from '@emotion/styled';
  2. import {BarChart} from 'sentry/components/charts/barChart';
  3. import type {BaseChartProps} from 'sentry/components/charts/baseChart';
  4. import ErrorPanel from 'sentry/components/charts/errorPanel';
  5. import TransitionChart from 'sentry/components/charts/transitionChart';
  6. import type {SelectOption} from 'sentry/components/compactSelect';
  7. import {CompactSelect} from 'sentry/components/compactSelect';
  8. import {IconWarning} from 'sentry/icons/iconWarning';
  9. import {space} from 'sentry/styles/space';
  10. import type {Series} from 'sentry/types/echarts';
  11. import {defined} from 'sentry/utils';
  12. import {
  13. axisLabelFormatter,
  14. getDurationUnit,
  15. tooltipFormatter,
  16. } from 'sentry/utils/discover/charts';
  17. import {aggregateOutputType} from 'sentry/utils/discover/fields';
  18. import {decodeScalar} from 'sentry/utils/queryString';
  19. import {useLocation} from 'sentry/utils/useLocation';
  20. import useRouter from 'sentry/utils/useRouter';
  21. import {formatVersion} from 'sentry/utils/versions/formatVersion';
  22. import {LoadingScreen} from 'sentry/views/insights/common/components/chart';
  23. import MiniChartPanel from 'sentry/views/insights/common/components/miniChartPanel';
  24. export type ChartSelectOptions = {
  25. title: string;
  26. yAxis: string;
  27. series?: Series[];
  28. subtitle?: string;
  29. xAxisLabel?: string[];
  30. };
  31. export function ScreensBarChart({
  32. chartHeight,
  33. chartKey,
  34. chartOptions,
  35. isLoading,
  36. chartProps,
  37. }: {
  38. chartKey: string;
  39. chartOptions: ChartSelectOptions[];
  40. chartHeight?: number;
  41. chartProps?: BaseChartProps;
  42. isLoading?: boolean;
  43. }) {
  44. const location = useLocation();
  45. const router = useRouter();
  46. const yAxis = decodeScalar(location.query[chartKey]);
  47. const selectedDisplay = yAxis ? chartOptions.findIndex(o => o.yAxis === yAxis) : 0;
  48. const menuOptions: SelectOption<string>[] = [];
  49. for (const option of chartOptions) {
  50. menuOptions.push({
  51. value: option.yAxis,
  52. label: option.title,
  53. disabled: chartOptions[selectedDisplay]?.title === option.title,
  54. });
  55. }
  56. return (
  57. <MiniChartPanel>
  58. <HeaderContainer>
  59. <Header>
  60. <ChartLabel>
  61. {chartOptions.length > 1 ? (
  62. <StyledCompactSelect
  63. options={menuOptions}
  64. value={chartOptions[selectedDisplay]?.yAxis}
  65. onChange={option => {
  66. const chartOption = chartOptions.find(o => o.yAxis === option.value);
  67. if (defined(chartOption)) {
  68. router.replace({
  69. pathname: router.location.pathname,
  70. query: {...router.location.query, [chartKey]: chartOption.yAxis},
  71. });
  72. }
  73. }}
  74. triggerProps={{
  75. borderless: true,
  76. size: 'zero',
  77. 'aria-label': chartOptions[selectedDisplay]?.title,
  78. }}
  79. offset={4}
  80. />
  81. ) : (
  82. chartOptions[selectedDisplay]?.title
  83. )}
  84. </ChartLabel>
  85. </Header>
  86. {chartOptions[selectedDisplay].subtitle && (
  87. <Subtitle>{chartOptions[selectedDisplay].subtitle}</Subtitle>
  88. )}
  89. </HeaderContainer>
  90. <TransitionChart
  91. loading={Boolean(isLoading)}
  92. reloading={Boolean(isLoading)}
  93. height={chartHeight ? `${chartHeight}px` : undefined}
  94. >
  95. <LoadingScreen loading={Boolean(isLoading)} />
  96. {selectedDisplay === -1 ? (
  97. <ErrorPanel height={`${chartHeight ?? 180}px`}>
  98. <IconWarning color="gray300" size="lg" />
  99. </ErrorPanel>
  100. ) : (
  101. <BarChart
  102. {...chartProps}
  103. height={chartHeight ?? 180}
  104. series={
  105. chartOptions[selectedDisplay].series?.map(series => ({
  106. ...series,
  107. name: formatVersion(series.seriesName),
  108. })) ?? []
  109. }
  110. grid={{
  111. left: '0',
  112. right: '0',
  113. top: '8px',
  114. bottom: '0',
  115. containLabel: true,
  116. }}
  117. xAxis={{
  118. type: 'category',
  119. axisTick: {show: true},
  120. data: chartOptions[selectedDisplay].xAxisLabel,
  121. truncate: 14,
  122. axisLabel: {
  123. interval: 0,
  124. },
  125. }}
  126. yAxis={{
  127. axisLabel: {
  128. formatter(value: number) {
  129. return axisLabelFormatter(
  130. value,
  131. aggregateOutputType(chartOptions[selectedDisplay].yAxis),
  132. undefined,
  133. getDurationUnit(chartOptions[selectedDisplay].series ?? [])
  134. );
  135. },
  136. },
  137. }}
  138. tooltip={{
  139. valueFormatter: (value, _seriesName) => {
  140. return tooltipFormatter(
  141. value,
  142. aggregateOutputType(chartOptions[selectedDisplay].yAxis)
  143. );
  144. },
  145. }}
  146. />
  147. )}
  148. </TransitionChart>
  149. </MiniChartPanel>
  150. );
  151. }
  152. const ChartLabel = styled('p')`
  153. ${p => p.theme.text.cardTitle}
  154. `;
  155. const HeaderContainer = styled('div')`
  156. padding: 0 ${space(1)} 0 0;
  157. `;
  158. const Header = styled('div')`
  159. min-height: 24px;
  160. width: 100%;
  161. display: flex;
  162. align-items: center;
  163. justify-content: space-between;
  164. `;
  165. const StyledCompactSelect = styled(CompactSelect)`
  166. /* Reset font-weight set by HeaderTitleLegend, buttons are already bold and
  167. * setting this higher up causes it to trickle into the menues */
  168. font-weight: ${p => p.theme.fontWeightNormal};
  169. margin: -${space(0.5)} -${space(1)} -${space(0.25)};
  170. min-width: 0;
  171. button {
  172. padding: ${space(0.5)} ${space(1)};
  173. font-size: ${p => p.theme.fontSizeLarge};
  174. }
  175. `;
  176. const Subtitle = styled('span')`
  177. color: ${p => p.theme.gray300};
  178. font-size: ${p => p.theme.fontSizeSmall};
  179. display: inline-block;
  180. `;