link.tsx 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. import {forwardRef, useEffect} from 'react';
  2. // eslint-disable-next-line no-restricted-imports
  3. import {Link as RouterLink, withRouter, WithRouterProps} from 'react-router';
  4. import styled from '@emotion/styled';
  5. import * as Sentry from '@sentry/react';
  6. import {Location, LocationDescriptor} from 'history';
  7. import {normalizeUrl} from 'sentry/utils/withDomainRequired';
  8. import {linkStyles} from './styles';
  9. export interface LinkProps
  10. extends Omit<
  11. React.DetailedHTMLProps<React.HTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>,
  12. 'href' | 'target' | 'as' | 'css'
  13. > {
  14. /**
  15. * The string path or LocationDescriptor object
  16. */
  17. to: ((location: Location) => LocationDescriptor) | LocationDescriptor;
  18. /**
  19. * Style applied to the component's root
  20. */
  21. className?: string;
  22. /**
  23. * Indicator if the link should be disabled
  24. */
  25. disabled?: boolean;
  26. /**
  27. * Forwarded ref
  28. */
  29. forwardedRef?: React.Ref<HTMLAnchorElement>;
  30. }
  31. /**
  32. * A context-aware version of Link (from react-router) that falls
  33. * back to <a> if there is no router present
  34. */
  35. interface WithRouterBaseLinkProps extends WithRouterProps, LinkProps {}
  36. function BaseLink({
  37. location,
  38. disabled,
  39. to,
  40. forwardedRef,
  41. router: _router,
  42. params: _params,
  43. routes: _routes,
  44. ...props
  45. }: WithRouterBaseLinkProps): React.ReactElement {
  46. useEffect(() => {
  47. // check if the router is present
  48. if (!location) {
  49. Sentry.captureException(
  50. new Error('The link component was rendered without being wrapped by a <Router />')
  51. );
  52. }
  53. }, [location]);
  54. to = normalizeUrl(to, location);
  55. if (!disabled && location) {
  56. return <RouterLink to={to} ref={forwardedRef as any} {...props} />;
  57. }
  58. return <a href={typeof to === 'string' ? to : ''} ref={forwardedRef} {...props} />;
  59. }
  60. // Re-assign to Link to make auto-importing smarter
  61. const Link = withRouter(
  62. styled(
  63. forwardRef<HTMLAnchorElement, Omit<WithRouterBaseLinkProps, 'forwardedRef'>>(
  64. (props, ref) => <BaseLink forwardedRef={ref} {...props} />
  65. )
  66. )`
  67. ${linkStyles}
  68. `
  69. );
  70. export default Link;