releaseSelector.tsx 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. import {useState} from 'react';
  2. import {browserHistory} from 'react-router';
  3. import styled from '@emotion/styled';
  4. import debounce from 'lodash/debounce';
  5. import {CompactSelect, SelectOption} from 'sentry/components/compactSelect';
  6. import PageFilterBar from 'sentry/components/organizations/pageFilterBar';
  7. import {DEFAULT_DEBOUNCE_DURATION} from 'sentry/constants';
  8. import {t, tn} from 'sentry/locale';
  9. import {space} from 'sentry/styles/space';
  10. import {defined} from 'sentry/utils';
  11. import {useLocation} from 'sentry/utils/useLocation';
  12. import {
  13. useReleases,
  14. useReleaseSelection,
  15. useReleaseStats,
  16. } from 'sentry/views/starfish/queries/useReleases';
  17. type Props = {
  18. selectorKey: string;
  19. selectorName?: string;
  20. selectorValue?: string;
  21. };
  22. export function ReleaseSelector({selectorName, selectorKey, selectorValue}: Props) {
  23. const [searchTerm, setSearchTerm] = useState<string | undefined>(undefined);
  24. const {data, isLoading} = useReleases(searchTerm);
  25. const {data: releaseStats} = useReleaseStats();
  26. const location = useLocation();
  27. const options: SelectOption<string>[] = [];
  28. if (defined(selectorValue)) {
  29. options.push({
  30. value: selectorValue,
  31. label: selectorValue,
  32. });
  33. }
  34. data
  35. ?.filter(({version}) => selectorValue !== version)
  36. .forEach(release => {
  37. const option = {
  38. value: release.version,
  39. label: release.version,
  40. details: (
  41. <LabelDetails sessionCount={releaseStats[release.version]?.['sum(session)']} />
  42. ),
  43. };
  44. options.push(option);
  45. });
  46. return (
  47. <StyledCompactSelect
  48. triggerProps={{
  49. prefix: selectorName,
  50. title: selectorValue,
  51. }}
  52. loading={isLoading}
  53. searchable
  54. value={selectorValue}
  55. options={[
  56. {
  57. value: '_releases',
  58. label: t('Sorted by date created'),
  59. options,
  60. },
  61. ]}
  62. onSearch={debounce(val => {
  63. setSearchTerm(val);
  64. }, DEFAULT_DEBOUNCE_DURATION)}
  65. onChange={newValue => {
  66. browserHistory.push({
  67. ...location,
  68. query: {
  69. ...location.query,
  70. [selectorKey]: newValue.value,
  71. },
  72. });
  73. }}
  74. onClose={() => {
  75. setSearchTerm(undefined);
  76. }}
  77. />
  78. );
  79. }
  80. type LabelDetailsProps = {
  81. sessionCount?: number;
  82. };
  83. function LabelDetails(props: LabelDetailsProps) {
  84. return (
  85. <DetailsContainer>
  86. <div>
  87. {defined(props.sessionCount)
  88. ? tn('%s session', '%s sessions', props.sessionCount)
  89. : '-'}
  90. </div>
  91. </DetailsContainer>
  92. );
  93. }
  94. export function ReleaseComparisonSelector() {
  95. const {primaryRelease, secondaryRelease} = useReleaseSelection();
  96. return (
  97. <PageFilterBar condensed>
  98. <ReleaseSelector selectorKey="primaryRelease" selectorValue={primaryRelease} />
  99. <ReleaseSelector
  100. selectorKey="secondaryRelease"
  101. selectorName={t('Compared To')}
  102. selectorValue={secondaryRelease}
  103. />
  104. </PageFilterBar>
  105. );
  106. }
  107. const StyledCompactSelect = styled(CompactSelect)`
  108. @media (min-width: ${p => p.theme.breakpoints.medium}) {
  109. max-width: 275px;
  110. }
  111. `;
  112. const DetailsContainer = styled('div')`
  113. display: flex;
  114. flex-direction: row;
  115. justify-content: space-between;
  116. gap: ${space(1)};
  117. `;