metricsRibbon.tsx 3.6 KB

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