link.tsx 1.9 KB

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