focusTabs.tsx 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. import type {ReactNode} from 'react';
  2. import {Fragment} from 'react';
  3. import styled from '@emotion/styled';
  4. import queryString from 'query-string';
  5. import FeatureBadge from 'sentry/components/featureBadge';
  6. import ExternalLink from 'sentry/components/links/externalLink';
  7. import ListLink from 'sentry/components/links/listLink';
  8. import ScrollableTabs from 'sentry/components/replays/scrollableTabs';
  9. import {Tooltip} from 'sentry/components/tooltip';
  10. import {t} from 'sentry/locale';
  11. import type {Organization} from 'sentry/types';
  12. import {trackAnalytics} from 'sentry/utils/analytics';
  13. import useActiveReplayTab, {TabKey} from 'sentry/utils/replays/hooks/useActiveReplayTab';
  14. import {useLocation} from 'sentry/utils/useLocation';
  15. import useOrganization from 'sentry/utils/useOrganization';
  16. function getReplayTabs(organization: Organization): Record<TabKey, ReactNode> {
  17. // The new Accessibility tab:
  18. const hasA11yTab = organization.features.includes('session-replay-a11y-tab');
  19. // The new trace table inside Breadcrumb items:
  20. const hasTraceTable = organization.features.includes('session-replay-trace-table');
  21. return {
  22. [TabKey.BREADCRUMBS]: t('Breadcrumbs'),
  23. [TabKey.CONSOLE]: t('Console'),
  24. [TabKey.NETWORK]: t('Network'),
  25. [TabKey.ERRORS]: t('Errors'),
  26. [TabKey.TRACE]: hasTraceTable ? null : t('Trace'),
  27. [TabKey.PERF]: null,
  28. [TabKey.A11Y]: hasA11yTab ? (
  29. <Fragment>
  30. <Tooltip
  31. isHoverable
  32. title={
  33. <ExternalLink
  34. href="https://developer.mozilla.org/en-US/docs/Learn/Accessibility/What_is_accessibility"
  35. onClick={e => {
  36. e.stopPropagation();
  37. }}
  38. >
  39. {t('What is accessibility?')}
  40. </ExternalLink>
  41. }
  42. >
  43. {t('Accessibility')}
  44. </Tooltip>
  45. <FlexFeatureBadge
  46. type="alpha"
  47. title={t('This feature is available for early adopters and may change')}
  48. />
  49. </Fragment>
  50. ) : null,
  51. [TabKey.MEMORY]: t('Memory'),
  52. [TabKey.TAGS]: t('Tags'),
  53. };
  54. }
  55. type Props = {
  56. className?: string;
  57. };
  58. function FocusTabs({className}: Props) {
  59. const organization = useOrganization();
  60. const {pathname, query} = useLocation();
  61. const {getActiveTab, setActiveTab} = useActiveReplayTab();
  62. const activeTab = getActiveTab();
  63. return (
  64. <ScrollableTabs className={className} underlined>
  65. {Object.entries(getReplayTabs(organization)).map(([tab, label]) =>
  66. label ? (
  67. <ListLink
  68. data-test-id={`replay-details-${tab}-btn`}
  69. key={tab}
  70. isActive={() => tab === activeTab}
  71. to={`${pathname}?${queryString.stringify({...query, t_main: tab})}`}
  72. onClick={e => {
  73. e.preventDefault();
  74. setActiveTab(tab);
  75. trackAnalytics('replay.details-tab-changed', {
  76. tab,
  77. organization,
  78. });
  79. }}
  80. >
  81. {label}
  82. </ListLink>
  83. ) : null
  84. )}
  85. </ScrollableTabs>
  86. );
  87. }
  88. const FlexFeatureBadge = styled(FeatureBadge)`
  89. & > span {
  90. display: flex;
  91. }
  92. `;
  93. export default FocusTabs;