focusTabs.tsx 2.8 KB

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