releaseSelector.tsx 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  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 {getFormattedDate} from 'sentry/utils/dates';
  12. import {useLocation} from 'sentry/utils/useLocation';
  13. import {
  14. useReleases,
  15. useReleaseSelection,
  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 location = useLocation();
  26. const options: SelectOption<string>[] = [];
  27. if (defined(selectorValue)) {
  28. const index = data?.findIndex(({version}) => version === selectorValue);
  29. const selectedRelease = defined(index) ? data?.[index] : undefined;
  30. let selectedReleaseSessionCount: number | undefined = undefined;
  31. let selectedReleaseDateCreated: string | undefined = undefined;
  32. if (defined(selectedRelease)) {
  33. selectedReleaseSessionCount = selectedRelease['sum(session)'];
  34. selectedReleaseDateCreated = selectedRelease.dateCreated;
  35. }
  36. options.push({
  37. value: selectorValue,
  38. label: selectorValue,
  39. details: (
  40. <LabelDetails
  41. sessionCount={selectedReleaseSessionCount}
  42. dateCreated={selectedReleaseDateCreated}
  43. />
  44. ),
  45. });
  46. }
  47. data
  48. ?.filter(({version}) => selectorValue !== version)
  49. .forEach(release => {
  50. const option = {
  51. value: release.version,
  52. label: release.version,
  53. details: (
  54. <LabelDetails
  55. sessionCount={release['sum(session)']}
  56. dateCreated={release.dateCreated}
  57. />
  58. ),
  59. };
  60. options.push(option);
  61. });
  62. return (
  63. <StyledCompactSelect
  64. triggerProps={{
  65. prefix: selectorName,
  66. title: selectorValue,
  67. }}
  68. loading={isLoading}
  69. searchable
  70. value={selectorValue}
  71. options={[
  72. {
  73. value: '_releases',
  74. label: t('Sorted by session count'),
  75. options,
  76. },
  77. ]}
  78. onSearch={debounce(val => {
  79. setSearchTerm(val);
  80. }, DEFAULT_DEBOUNCE_DURATION)}
  81. onChange={newValue => {
  82. browserHistory.push({
  83. ...location,
  84. query: {
  85. ...location.query,
  86. [selectorKey]: newValue.value,
  87. },
  88. });
  89. }}
  90. onClose={() => {
  91. setSearchTerm(undefined);
  92. }}
  93. />
  94. );
  95. }
  96. type LabelDetailsProps = {
  97. dateCreated?: string;
  98. sessionCount?: number;
  99. };
  100. function LabelDetails(props: LabelDetailsProps) {
  101. return (
  102. <DetailsContainer>
  103. <div>
  104. {defined(props.sessionCount)
  105. ? tn('%s session', '%s sessions', props.sessionCount)
  106. : t('No sessions')}
  107. </div>
  108. <div>
  109. {defined(props.dateCreated)
  110. ? getFormattedDate(props.dateCreated, 'MMM D, YYYY')
  111. : null}
  112. </div>
  113. </DetailsContainer>
  114. );
  115. }
  116. export function ReleaseComparisonSelector() {
  117. const {primaryRelease, secondaryRelease} = useReleaseSelection();
  118. return (
  119. <PageFilterBar condensed>
  120. <ReleaseSelector
  121. selectorKey="primaryRelease"
  122. selectorValue={primaryRelease}
  123. selectorName={t('Release 1')}
  124. key="primaryRelease"
  125. />
  126. <ReleaseSelector
  127. selectorKey="secondaryRelease"
  128. selectorName={t('Release 2')}
  129. selectorValue={secondaryRelease}
  130. key="secondaryRelease"
  131. />
  132. </PageFilterBar>
  133. );
  134. }
  135. const StyledCompactSelect = styled(CompactSelect)`
  136. @media (min-width: ${p => p.theme.breakpoints.medium}) {
  137. max-width: 275px;
  138. }
  139. `;
  140. const DetailsContainer = styled('div')`
  141. display: flex;
  142. flex-direction: row;
  143. justify-content: space-between;
  144. gap: ${space(1)};
  145. `;