alertLink.tsx 2.9 KB

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