detailedError.tsx 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. import {Component} from 'react';
  2. import * as Sentry from '@sentry/react';
  3. import classNames from 'classnames';
  4. import Button from 'sentry/components/button';
  5. import {IconFlag} from 'sentry/icons';
  6. import {t} from 'sentry/locale';
  7. type DefaultProps = {
  8. /**
  9. * Hide support links in footer of error message
  10. */
  11. hideSupportLinks: boolean;
  12. };
  13. type Props = DefaultProps & {
  14. /**
  15. * Error heading
  16. */
  17. heading: React.ReactNode;
  18. className?: string;
  19. /**
  20. * Detailed error explanation
  21. */
  22. message?: React.ReactNode;
  23. /**
  24. * Retry callback
  25. */
  26. onRetry?: (e: React.MouseEvent<HTMLAnchorElement>) => void;
  27. };
  28. function openFeedback(e: React.MouseEvent) {
  29. e.preventDefault();
  30. Sentry.showReportDialog();
  31. }
  32. class DetailedError extends Component<Props> {
  33. static defaultProps: DefaultProps = {
  34. hideSupportLinks: false,
  35. };
  36. componentDidMount() {
  37. // XXX(epurkhiser): Why is this here?
  38. this.forceUpdateTimeout = window.setTimeout(() => this.forceUpdate(), 100);
  39. }
  40. componentWillUnmount() {
  41. window.clearTimeout(this.forceUpdateTimeout);
  42. }
  43. forceUpdateTimeout: number | undefined = undefined;
  44. render() {
  45. const {className, heading, message, onRetry, hideSupportLinks} = this.props;
  46. const cx = classNames('detailed-error', className);
  47. const showFooter = !!onRetry || !hideSupportLinks;
  48. return (
  49. <div className={cx}>
  50. <div className="detailed-error-icon">
  51. <IconFlag size="lg" />
  52. </div>
  53. <div className="detailed-error-content">
  54. <h4>{heading}</h4>
  55. <div className="detailed-error-content-body">{message}</div>
  56. {showFooter && (
  57. <div className="detailed-error-content-footer">
  58. <div>
  59. {onRetry && (
  60. <a onClick={onRetry} className="btn btn-default">
  61. {t('Retry')}
  62. </a>
  63. )}
  64. </div>
  65. {!hideSupportLinks && (
  66. <div className="detailed-error-support-links">
  67. {Sentry.lastEventId() && (
  68. <Button priority="link" onClick={openFeedback}>
  69. {t('Fill out a report')}
  70. </Button>
  71. )}
  72. <a href="https://status.sentry.io/">{t('Service status')}</a>
  73. <a href="https://sentry.io/support/">{t('Contact support')}</a>
  74. </div>
  75. )}
  76. </div>
  77. )}
  78. </div>
  79. </div>
  80. );
  81. }
  82. }
  83. export default DetailedError;