banner.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. import {useState} from 'react';
  2. import {css} from '@emotion/react';
  3. import styled from '@emotion/styled';
  4. import {Button} from 'sentry/components/button';
  5. import ButtonBar from 'sentry/components/buttonBar';
  6. import {IconClose} from 'sentry/icons';
  7. import {t} from 'sentry/locale';
  8. import {space} from 'sentry/styles/space';
  9. const makeKey = (prefix: string) => `${prefix}-banner-dismissed`;
  10. function dismissBanner(bannerKey: string) {
  11. localStorage.setItem(makeKey(bannerKey), 'true');
  12. }
  13. function useDismissable(bannerKey: string) {
  14. const key = makeKey(bannerKey);
  15. const [value, setValue] = useState(localStorage.getItem(key));
  16. const dismiss = () => {
  17. setValue('true');
  18. dismissBanner(bannerKey);
  19. };
  20. return [value === 'true', dismiss] as const;
  21. }
  22. type BannerWrapperProps = {
  23. backgroundComponent?: React.ReactNode;
  24. backgroundImg?: string;
  25. };
  26. type Props = BannerWrapperProps & {
  27. children?: React.ReactNode;
  28. className?: string;
  29. dismissKey?: string;
  30. isDismissable?: boolean;
  31. subtitle?: string;
  32. title?: string;
  33. };
  34. function Banner({
  35. title,
  36. subtitle,
  37. isDismissable = true,
  38. dismissKey = 'generic-banner',
  39. className,
  40. backgroundImg,
  41. backgroundComponent,
  42. children,
  43. }: Props) {
  44. const [dismissed, dismiss] = useDismissable(dismissKey);
  45. if (dismissed) {
  46. return null;
  47. }
  48. return (
  49. <BannerWrapper backgroundImg={backgroundImg} className={className}>
  50. {backgroundComponent}
  51. {isDismissable ? <CloseButton onClick={dismiss} aria-label={t('Close')} /> : null}
  52. <BannerContent>
  53. <BannerTitle>{title}</BannerTitle>
  54. <BannerSubtitle>{subtitle}</BannerSubtitle>
  55. <StyledButtonBar gap={1}>{children}</StyledButtonBar>
  56. </BannerContent>
  57. </BannerWrapper>
  58. );
  59. }
  60. Banner.dismiss = dismissBanner;
  61. const BannerWrapper = styled('div')<BannerWrapperProps>`
  62. ${p =>
  63. p.backgroundImg
  64. ? css`
  65. background: url(${p.backgroundImg});
  66. background-repeat: no-repeat;
  67. background-size: cover;
  68. background-position: center center;
  69. `
  70. : css`
  71. background-color: ${p.theme.gray500};
  72. `}
  73. display: flex;
  74. overflow: hidden;
  75. align-items: center;
  76. justify-content: center;
  77. position: relative;
  78. margin-bottom: ${space(2)};
  79. box-shadow: ${p => p.theme.dropShadowMedium};
  80. border-radius: ${p => p.theme.borderRadius};
  81. height: 180px;
  82. color: ${p => p.theme.white};
  83. @media (min-width: ${p => p.theme.breakpoints.small}) {
  84. height: 220px;
  85. }
  86. `;
  87. const BannerContent = styled('div')`
  88. position: absolute;
  89. display: grid;
  90. justify-items: center;
  91. grid-template-rows: repeat(3, max-content);
  92. text-align: center;
  93. padding: ${space(4)};
  94. `;
  95. const BannerTitle = styled('h1')`
  96. margin: 0;
  97. @media (min-width: ${p => p.theme.breakpoints.small}) {
  98. font-size: 40px;
  99. }
  100. `;
  101. const BannerSubtitle = styled('div')`
  102. margin: 0;
  103. @media (min-width: ${p => p.theme.breakpoints.small}) {
  104. font-size: ${p => p.theme.fontSizeExtraLarge};
  105. }
  106. `;
  107. const StyledButtonBar = styled(ButtonBar)`
  108. margin-top: ${space(2)};
  109. width: fit-content;
  110. `;
  111. const CloseButton = styled(Button)`
  112. position: absolute;
  113. display: block;
  114. top: ${space(2)};
  115. right: ${space(2)};
  116. color: ${p => p.theme.white};
  117. cursor: pointer;
  118. z-index: 1;
  119. `;
  120. CloseButton.defaultProps = {
  121. icon: <IconClose />,
  122. ['aria-label']: t('Close'),
  123. priority: 'link',
  124. borderless: true,
  125. size: 'xs',
  126. };
  127. export default Banner;