sortableHeader.tsx 3.0 KB

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