index.tsx 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. import styled from '@emotion/styled';
  2. import capitalize from 'lodash/capitalize';
  3. import List from 'sentry/components/list';
  4. import ListItem from 'sentry/components/list/listItem';
  5. import Tooltip from 'sentry/components/tooltip';
  6. import {IconWarning} from 'sentry/icons';
  7. import {t} from 'sentry/locale';
  8. import space from 'sentry/styles/space';
  9. import {Meta, MetaError} from 'sentry/types';
  10. import Chunks from './chunks';
  11. import {getTooltipText} from './utils';
  12. import ValueElement from './valueElement';
  13. type Props = {
  14. value: React.ReactNode;
  15. className?: string;
  16. meta?: Meta;
  17. };
  18. const AnnotatedText = ({value, meta, className, ...props}: Props) => {
  19. const renderValue = () => {
  20. if (meta?.chunks?.length && meta.chunks.length > 1) {
  21. return <Chunks chunks={meta.chunks} />;
  22. }
  23. const element = <ValueElement value={value} meta={meta} />;
  24. if (meta?.rem?.length) {
  25. const title = getTooltipText({rule_id: meta.rem[0][0], remark: meta.rem[0][1]});
  26. return <Tooltip title={title}>{element}</Tooltip>;
  27. }
  28. return element;
  29. };
  30. const formatErrorKind = (kind: string) => {
  31. return capitalize(kind.replace(/_/g, ' '));
  32. };
  33. const getErrorMessage = (error: MetaError) => {
  34. const errorMessage: string[] = [];
  35. if (Array.isArray(error)) {
  36. if (error[0]) {
  37. errorMessage.push(formatErrorKind(error[0]));
  38. }
  39. if (error[1]?.reason) {
  40. errorMessage.push(`(${error[1].reason})`);
  41. }
  42. } else {
  43. errorMessage.push(formatErrorKind(error));
  44. }
  45. return errorMessage.join(' ');
  46. };
  47. const getTooltipTitle = (errors: Array<MetaError>) => {
  48. if (errors.length === 1) {
  49. return <TooltipTitle>{t('Error: %s', getErrorMessage(errors[0]))}</TooltipTitle>;
  50. }
  51. return (
  52. <TooltipTitle>
  53. <span>{t('Errors:')}</span>
  54. <StyledList symbol="bullet">
  55. {errors.map((error, index) => (
  56. <ListItem key={index}>{getErrorMessage(error)}</ListItem>
  57. ))}
  58. </StyledList>
  59. </TooltipTitle>
  60. );
  61. };
  62. const renderErrors = (errors: Array<MetaError>) => {
  63. if (!errors.length) {
  64. return null;
  65. }
  66. return (
  67. <StyledTooltipError title={getTooltipTitle(errors)}>
  68. <StyledIconWarning color="red300" />
  69. </StyledTooltipError>
  70. );
  71. };
  72. return (
  73. <span role="text" className={className} {...props}>
  74. {renderValue()}
  75. {meta?.err && renderErrors(meta.err)}
  76. </span>
  77. );
  78. };
  79. export default AnnotatedText;
  80. const StyledTooltipError = styled(Tooltip)`
  81. margin-left: ${space(0.75)};
  82. vertical-align: middle;
  83. `;
  84. const StyledList = styled(List)`
  85. li {
  86. padding-left: ${space(3)};
  87. word-break: break-all;
  88. :before {
  89. border-color: ${p => p.theme.white};
  90. top: 6px;
  91. }
  92. }
  93. `;
  94. const TooltipTitle = styled('div')`
  95. text-align: left;
  96. `;
  97. const StyledIconWarning = styled(IconWarning)`
  98. vertical-align: middle;
  99. `;