link.tsx 2.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
  1. import {forwardRef, useContext} from 'react';
  2. import {Link as RouterLink} from 'react-router';
  3. import styled from '@emotion/styled';
  4. import {Location, LocationDescriptor} from 'history';
  5. import {normalizeUrl} from 'sentry/utils/withDomainRequired';
  6. import {RouteContext} from 'sentry/views/routeContext';
  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. * If your link target is a string literal or a `LocationDescriptor` with
  17. * a literal `pathname`, you need to use the slug based URL
  18. * e.g `/organizations/${slug}/issues/`. This ensures that your link will
  19. * work in environments that do have customer-domains (saas) and those without
  20. * customer-domains (single-tenant).
  21. */
  22. to: ((location: Location) => LocationDescriptor) | LocationDescriptor;
  23. /**
  24. * Style applied to the component's root
  25. */
  26. className?: string;
  27. /**
  28. * Indicator if the link should be disabled
  29. */
  30. disabled?: boolean;
  31. /**
  32. * Forwarded ref
  33. */
  34. forwardedRef?: React.Ref<HTMLAnchorElement>;
  35. }
  36. /**
  37. * A context-aware version of Link (from react-router) that falls
  38. * back to <a> if there is no router present
  39. */
  40. function BaseLink({disabled, to, forwardedRef, ...props}: LinkProps): React.ReactElement {
  41. const route = useContext(RouteContext);
  42. const location = route?.location;
  43. to = normalizeUrl(to, location);
  44. if (!disabled && location) {
  45. return <RouterLink to={to} ref={forwardedRef as any} {...props} />;
  46. }
  47. return <a href={typeof to === 'string' ? to : ''} ref={forwardedRef} {...props} />;
  48. }
  49. // Re-assign to Link to make auto-importing smarter
  50. const Link = styled(
  51. forwardRef<HTMLAnchorElement, Omit<LinkProps, 'forwardedRef'>>((props, ref) => (
  52. <BaseLink forwardedRef={ref} {...props} />
  53. ))
  54. )`
  55. ${linkStyles}
  56. `;
  57. export default Link;