notification.tsx 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. import classNames from 'classnames'
  2. import React, { ReactElement, useState } from 'react'
  3. import { useTranslation } from 'react-i18next'
  4. import MaterialIcon from './material-icon'
  5. export type NotificationType =
  6. | 'info'
  7. | 'success'
  8. | 'warning'
  9. | 'error'
  10. | 'offer'
  11. export type NotificationProps = {
  12. action?: React.ReactElement
  13. ariaLive?: 'polite' | 'off' | 'assertive'
  14. className?: string
  15. content: React.ReactElement | string
  16. customIcon?: React.ReactElement
  17. disclaimer?: React.ReactElement | string
  18. isDismissible?: boolean
  19. isActionBelowContent?: boolean
  20. onDismiss?: () => void
  21. title?: string
  22. type: NotificationType
  23. id?: string
  24. }
  25. function NotificationIcon({
  26. notificationType,
  27. customIcon,
  28. }: {
  29. notificationType: NotificationType
  30. customIcon?: ReactElement
  31. }) {
  32. let icon = <MaterialIcon type="info" />
  33. if (customIcon) {
  34. icon = customIcon
  35. } else if (notificationType === 'success') {
  36. icon = <MaterialIcon type="check_circle" />
  37. } else if (notificationType === 'warning') {
  38. icon = <MaterialIcon type="warning" />
  39. } else if (notificationType === 'error') {
  40. icon = <MaterialIcon type="error" />
  41. } else if (notificationType === 'offer') {
  42. icon = <MaterialIcon type="campaign" />
  43. }
  44. return <div className="notification-icon">{icon}</div>
  45. }
  46. function Notification({
  47. action,
  48. ariaLive,
  49. className = '',
  50. content,
  51. customIcon,
  52. disclaimer,
  53. isActionBelowContent,
  54. isDismissible,
  55. onDismiss,
  56. title,
  57. type,
  58. id,
  59. }: NotificationProps) {
  60. type = type || 'info'
  61. const { t } = useTranslation()
  62. const [show, setShow] = useState(true)
  63. const notificationClassName = classNames(
  64. 'notification',
  65. `notification-type-${type}`,
  66. isActionBelowContent ? 'notification-cta-below-content' : '',
  67. className
  68. )
  69. const handleDismiss = () => {
  70. setShow(false)
  71. if (onDismiss) onDismiss()
  72. }
  73. if (!show) {
  74. return null
  75. }
  76. return (
  77. <div
  78. className={notificationClassName}
  79. aria-live={ariaLive || 'off'}
  80. role="alert"
  81. id={id}
  82. >
  83. <NotificationIcon notificationType={type} customIcon={customIcon} />
  84. <div className="notification-content-and-cta">
  85. <div className="notification-content">
  86. {title && (
  87. <p>
  88. <b>{title}</b>
  89. </p>
  90. )}
  91. {content}
  92. </div>
  93. {action && <div className="notification-cta">{action}</div>}
  94. {disclaimer && (
  95. <div className="notification-disclaimer">{disclaimer}</div>
  96. )}
  97. </div>
  98. {isDismissible && (
  99. <div className="notification-close-btn">
  100. <button aria-label={t('close')} onClick={handleDismiss}>
  101. <MaterialIcon type="close" />
  102. </button>
  103. </div>
  104. )}
  105. </div>
  106. )
  107. }
  108. export default Notification