alertLink.tsx 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. import styled from '@emotion/styled';
  2. import omit from 'lodash/omit';
  3. import ExternalLink from 'sentry/components/links/externalLink';
  4. import Link from 'sentry/components/links/link';
  5. import {IconChevron} from 'sentry/icons';
  6. import space from 'sentry/styles/space';
  7. type Size = 'small' | 'normal';
  8. type Priority = 'info' | 'warning' | 'success' | 'error' | 'muted';
  9. type LinkProps = React.ComponentPropsWithoutRef<typeof Link>;
  10. type OtherProps = {
  11. children?: React.ReactNode;
  12. ['data-test-id']?: string;
  13. icon?: string | React.ReactNode;
  14. onClick?: (e: React.MouseEvent) => void;
  15. };
  16. type DefaultProps = {
  17. openInNewTab: boolean;
  18. priority: Priority;
  19. size: Size;
  20. withoutMarginBottom: boolean;
  21. href?: string;
  22. };
  23. type Props = OtherProps & Partial<DefaultProps> & Partial<Pick<LinkProps, 'to'>>;
  24. type StyledLinkProps = DefaultProps &
  25. Partial<Pick<LinkProps, 'to'>> &
  26. Omit<LinkProps, 'to' | 'size'>;
  27. function AlertLink({
  28. size = 'normal',
  29. priority = 'warning',
  30. icon,
  31. children,
  32. onClick,
  33. withoutMarginBottom = false,
  34. openInNewTab = false,
  35. to,
  36. href,
  37. ['data-test-id']: dataTestId,
  38. }: Props) {
  39. return (
  40. <StyledLink
  41. data-test-id={dataTestId}
  42. to={to}
  43. href={href}
  44. onClick={onClick}
  45. size={size}
  46. priority={priority}
  47. withoutMarginBottom={withoutMarginBottom}
  48. openInNewTab={openInNewTab}
  49. >
  50. {icon && <IconWrapper>{icon}</IconWrapper>}
  51. <AlertLinkText>{children}</AlertLinkText>
  52. <IconLink>
  53. <IconChevron direction="right" />
  54. </IconLink>
  55. </StyledLink>
  56. );
  57. }
  58. export default AlertLink;
  59. const StyledLink = styled(({openInNewTab, to, href, ...props}: StyledLinkProps) => {
  60. const linkProps = omit(props, ['withoutMarginBottom', 'priority', 'size']);
  61. if (href) {
  62. return <ExternalLink {...linkProps} href={href} openInNewTab={openInNewTab} />;
  63. }
  64. return <Link {...linkProps} to={to || ''} />;
  65. })`
  66. display: flex;
  67. align-items: center;
  68. background-color: ${p => p.theme.alert[p.priority].backgroundLight};
  69. color: ${p => p.theme.textColor};
  70. font-size: ${p => p.theme.fontSizeMedium};
  71. border: 1px dashed ${p => p.theme.alert[p.priority].border};
  72. padding: ${p => (p.size === 'small' ? `${space(1)} ${space(1.5)}` : space(2))};
  73. margin-bottom: ${p => (p.withoutMarginBottom ? 0 : space(3))};
  74. border-radius: 0.25em;
  75. transition: 0.2s border-color;
  76. &.focus-visible {
  77. outline: none;
  78. box-shadow: ${p => p.theme.alert[p.priority].border}7f 0 0 0 2px;
  79. }
  80. `;
  81. const IconWrapper = styled('span')`
  82. display: flex;
  83. height: calc(${p => p.theme.fontSizeMedium} * ${p => p.theme.text.lineHeightBody});
  84. margin-right: ${space(1)};
  85. align-items: center;
  86. `;
  87. const IconLink = styled(IconWrapper)`
  88. margin-right: 0;
  89. margin-left: ${space(1)};
  90. `;
  91. const AlertLinkText = styled('div')`
  92. line-height: ${p => p.theme.text.lineHeightBody};
  93. flex-grow: 1;
  94. `;