metricsRibbon.tsx 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. import type {ComponentProps} from 'react';
  2. import {useMemo} from 'react';
  3. import styled from '@emotion/styled';
  4. import {space} from 'sentry/styles/space';
  5. import type {NewQuery} from 'sentry/types/organization';
  6. import type {TableData, TableDataRow} from 'sentry/utils/discover/discoverQuery';
  7. import EventView from 'sentry/utils/discover/eventView';
  8. import type {DiscoverDatasets} from 'sentry/utils/discover/types';
  9. import {MutableSearch} from 'sentry/utils/tokenizeSearch';
  10. import {useLocation} from 'sentry/utils/useLocation';
  11. import usePageFilters from 'sentry/utils/usePageFilters';
  12. import {MetricReadout} from 'sentry/views/performance/metricReadout';
  13. import {useTableQuery} from 'sentry/views/performance/mobile/screenload/screens/screensTable';
  14. import useCrossPlatformProject from 'sentry/views/performance/mobile/useCrossPlatformProject';
  15. import {useReleaseSelection} from 'sentry/views/starfish/queries/useReleases';
  16. import {appendReleaseFilters} from 'sentry/views/starfish/utils/releaseComparison';
  17. interface BlockProps {
  18. dataKey: string | ((data?: TableDataRow[]) => number | undefined);
  19. title: string;
  20. unit: ComponentProps<typeof MetricReadout>['unit'];
  21. allowZero?: boolean;
  22. }
  23. export function MetricsRibbon({
  24. filters,
  25. blocks,
  26. fields,
  27. referrer,
  28. dataset,
  29. }: {
  30. blocks: BlockProps[];
  31. dataset: DiscoverDatasets;
  32. fields: string[];
  33. referrer: string;
  34. filters?: string[];
  35. }) {
  36. const {selection} = usePageFilters();
  37. const location = useLocation();
  38. const {
  39. primaryRelease,
  40. secondaryRelease,
  41. isLoading: isReleasesLoading,
  42. } = useReleaseSelection();
  43. const {isProjectCrossPlatform, selectedPlatform} = useCrossPlatformProject();
  44. const queryString = useMemo(() => {
  45. const searchQuery = new MutableSearch([...(filters ?? [])]);
  46. if (isProjectCrossPlatform) {
  47. searchQuery.addFilterValue('os.name', selectedPlatform);
  48. }
  49. return appendReleaseFilters(searchQuery, primaryRelease, secondaryRelease);
  50. }, [
  51. filters,
  52. isProjectCrossPlatform,
  53. primaryRelease,
  54. secondaryRelease,
  55. selectedPlatform,
  56. ]);
  57. const newQuery: NewQuery = {
  58. name: 'ScreenMetricsRibbon',
  59. fields,
  60. query: queryString,
  61. dataset,
  62. version: 2,
  63. projects: selection.projects,
  64. };
  65. const eventView = EventView.fromNewQueryWithLocation(newQuery, location);
  66. const {data, isLoading} = useTableQuery({
  67. eventView,
  68. enabled: !isReleasesLoading,
  69. referrer,
  70. });
  71. return (
  72. <BlockContainer>
  73. {blocks.map(({title, dataKey, unit}) => (
  74. <MetricsBlock
  75. key={title}
  76. title={title}
  77. unit={unit}
  78. dataKey={dataKey}
  79. data={data}
  80. isLoading={isLoading}
  81. />
  82. ))}
  83. </BlockContainer>
  84. );
  85. }
  86. function MetricsBlock({
  87. title,
  88. unit,
  89. data,
  90. dataKey,
  91. isLoading,
  92. allowZero,
  93. }: {
  94. isLoading: boolean;
  95. title: string;
  96. data?: TableData;
  97. release?: string;
  98. } & BlockProps) {
  99. const value =
  100. typeof dataKey === 'function'
  101. ? dataKey(data?.data)
  102. : (data?.data?.[0]?.[dataKey] as number);
  103. const hasData = (value && value !== 0) || (value === 0 && allowZero);
  104. return (
  105. <MetricReadout
  106. title={title}
  107. align="left"
  108. value={hasData ? value : undefined}
  109. isLoading={isLoading}
  110. unit={unit}
  111. />
  112. );
  113. }
  114. const BlockContainer = styled('div')`
  115. display: flex;
  116. gap: ${space(2)};
  117. `;