sortableHeader.tsx 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. import styled from '@emotion/styled';
  2. import Link from 'sentry/components/links/link';
  3. import QuestionTooltip from 'sentry/components/questionTooltip';
  4. import {IconArrow} from 'sentry/icons';
  5. import {space} from 'sentry/styles/space';
  6. import trackAdvancedAnalyticsEvent from 'sentry/utils/analytics/trackAdvancedAnalyticsEvent';
  7. import type {Sort} from 'sentry/utils/discover/fields';
  8. import {useLocation} from 'sentry/utils/useLocation';
  9. import useOrganization from 'sentry/utils/useOrganization';
  10. import type {ReplayListLocationQuery} from 'sentry/views/replays/types';
  11. import {ReplayRecordNestedFieldName} from 'sentry/views/replays/types';
  12. type NotSortable = {
  13. label: string;
  14. tooltip?: string;
  15. };
  16. type Sortable = {
  17. fieldName: ReplayRecordNestedFieldName;
  18. label: string;
  19. sort: undefined | Sort;
  20. tooltip?: string;
  21. };
  22. type Props = NotSortable | Sortable;
  23. function SortableHeader(props: Props) {
  24. const location = useLocation<ReplayListLocationQuery>();
  25. const organization = useOrganization();
  26. if (!('sort' in props) || !props.sort) {
  27. const {label, tooltip} = props;
  28. return (
  29. <Header>
  30. {label}
  31. {tooltip ? (
  32. <StyledQuestionTooltip size="xs" position="top" title={tooltip} />
  33. ) : null}
  34. </Header>
  35. );
  36. }
  37. const {fieldName, label, sort, tooltip} = props;
  38. const arrowDirection = sort?.kind === 'asc' ? 'up' : 'down';
  39. const sortArrow = <IconArrow color="gray300" size="xs" direction={arrowDirection} />;
  40. return (
  41. <Header>
  42. <SortLink
  43. role="columnheader"
  44. aria-sort={
  45. sort?.field.endsWith(fieldName)
  46. ? sort?.kind === 'asc'
  47. ? 'ascending'
  48. : 'descending'
  49. : 'none'
  50. }
  51. onClick={() => {
  52. const column = sort?.field.endsWith(fieldName)
  53. ? sort?.kind === 'desc'
  54. ? fieldName
  55. : '-' + fieldName
  56. : '-' + fieldName;
  57. trackAdvancedAnalyticsEvent('replay.list-sorted', {
  58. organization,
  59. column,
  60. });
  61. }}
  62. to={{
  63. pathname: location.pathname,
  64. query: {
  65. ...location.query,
  66. sort: sort?.field.endsWith(fieldName)
  67. ? sort?.kind === 'desc'
  68. ? fieldName
  69. : '-' + fieldName
  70. : '-' + fieldName,
  71. },
  72. }}
  73. >
  74. {label} {sort?.field === fieldName && sortArrow}
  75. </SortLink>
  76. {tooltip ? (
  77. <StyledQuestionTooltip size="xs" position="top" title={tooltip} />
  78. ) : null}
  79. </Header>
  80. );
  81. }
  82. const Header = styled('div')`
  83. display: grid;
  84. grid-template-columns: repeat(2, max-content);
  85. align-items: center;
  86. padding: ${space(1.5)};
  87. `;
  88. const SortLink = styled(Link)`
  89. color: inherit;
  90. :hover {
  91. color: inherit;
  92. }
  93. svg {
  94. vertical-align: top;
  95. }
  96. `;
  97. const StyledQuestionTooltip = styled(QuestionTooltip)`
  98. margin-left: ${space(0.5)};
  99. `;
  100. export default SortableHeader;