symbol.tsx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. import styled from '@emotion/styled';
  2. import {STACKTRACE_PREVIEW_TOOLTIP_DELAY} from 'sentry/components/stacktracePreview';
  3. import Tooltip from 'sentry/components/tooltip';
  4. import {IconFilter} from 'sentry/icons';
  5. import {t} from 'sentry/locale';
  6. import space from 'sentry/styles/space';
  7. import {Frame} from 'sentry/types';
  8. import {defined} from 'sentry/utils';
  9. import FunctionName from './functionName';
  10. import GroupingIndicator from './groupingIndicator';
  11. import {getFrameHint} from './utils';
  12. type Props = {
  13. frame: Frame;
  14. absoluteFilePaths?: boolean;
  15. className?: string;
  16. /**
  17. * Is the stack trace being previewed in a hovercard?
  18. */
  19. isHoverPreviewed?: boolean;
  20. isUsedForGrouping?: boolean;
  21. nativeStackTraceV2?: boolean;
  22. onFunctionNameToggle?: (event: React.MouseEvent<SVGElement>) => void;
  23. showCompleteFunctionName?: boolean;
  24. };
  25. const Symbol = ({
  26. frame,
  27. absoluteFilePaths,
  28. onFunctionNameToggle,
  29. showCompleteFunctionName,
  30. nativeStackTraceV2,
  31. isHoverPreviewed,
  32. isUsedForGrouping,
  33. className,
  34. }: Props) => {
  35. const hasFunctionNameHiddenDetails =
  36. defined(frame.rawFunction) &&
  37. defined(frame.function) &&
  38. frame.function !== frame.rawFunction;
  39. const getFunctionNameTooltipTitle = () => {
  40. if (!hasFunctionNameHiddenDetails) {
  41. return undefined;
  42. }
  43. if (!showCompleteFunctionName) {
  44. return t('Expand function details');
  45. }
  46. return t('Hide function details');
  47. };
  48. const [hint, hintIcon] = getFrameHint(frame);
  49. const enablePathTooltip = defined(frame.absPath) && frame.absPath !== frame.filename;
  50. const functionNameTooltipTitle = getFunctionNameTooltipTitle();
  51. const tooltipDelay = isHoverPreviewed ? STACKTRACE_PREVIEW_TOOLTIP_DELAY : undefined;
  52. return (
  53. <Wrapper className={className}>
  54. {onFunctionNameToggle && (
  55. <FunctionNameToggleTooltip
  56. title={functionNameTooltipTitle}
  57. containerDisplayMode="inline-flex"
  58. delay={tooltipDelay}
  59. >
  60. <FunctionNameToggleIcon
  61. hasFunctionNameHiddenDetails={hasFunctionNameHiddenDetails}
  62. onClick={hasFunctionNameHiddenDetails ? onFunctionNameToggle : undefined}
  63. size="xs"
  64. color="purple300"
  65. />
  66. </FunctionNameToggleTooltip>
  67. )}
  68. <Data>
  69. <StyledFunctionName
  70. frame={frame}
  71. showCompleteFunctionName={showCompleteFunctionName}
  72. hasHiddenDetails={hasFunctionNameHiddenDetails}
  73. />
  74. {hint && (
  75. <HintStatus>
  76. <Tooltip title={hint} delay={tooltipDelay}>
  77. {hintIcon}
  78. </Tooltip>
  79. </HintStatus>
  80. )}
  81. {frame.filename &&
  82. (nativeStackTraceV2 ? (
  83. <Filename>
  84. {'('}
  85. {absoluteFilePaths ? frame.absPath : frame.filename}
  86. {frame.lineNo && `:${frame.lineNo}`}
  87. {')'}
  88. </Filename>
  89. ) : (
  90. <FileNameTooltip
  91. title={frame.absPath}
  92. disabled={!enablePathTooltip}
  93. delay={tooltipDelay}
  94. >
  95. <Filename>
  96. {'('}
  97. {frame.filename}
  98. {frame.lineNo && `:${frame.lineNo}`}
  99. {')'}
  100. </Filename>
  101. </FileNameTooltip>
  102. ))}
  103. {isUsedForGrouping && <GroupingIndicator />}
  104. </Data>
  105. </Wrapper>
  106. );
  107. };
  108. const Wrapper = styled('div')`
  109. text-align: left;
  110. grid-column-start: 1;
  111. grid-column-end: -1;
  112. order: 3;
  113. flex: 1;
  114. display: flex;
  115. code {
  116. background: transparent;
  117. color: ${p => p.theme.textColor};
  118. padding-right: ${space(0.5)};
  119. }
  120. @media (min-width: ${props => props.theme.breakpoints.small}) {
  121. order: 0;
  122. grid-column-start: auto;
  123. grid-column-end: auto;
  124. }
  125. `;
  126. const StyledFunctionName = styled(FunctionName)`
  127. margin-right: ${space(0.75)};
  128. `;
  129. const Data = styled('div')`
  130. max-width: 100%;
  131. display: flex;
  132. flex-wrap: wrap;
  133. align-items: center;
  134. `;
  135. const HintStatus = styled('span')`
  136. position: relative;
  137. top: ${space(0.25)};
  138. margin: 0 ${space(0.75)} 0 -${space(0.25)};
  139. `;
  140. const FileNameTooltip = styled(Tooltip)`
  141. margin-right: ${space(0.75)};
  142. `;
  143. const Filename = styled('span')`
  144. color: ${p => p.theme.purple300};
  145. `;
  146. export const FunctionNameToggleIcon = styled(IconFilter, {
  147. shouldForwardProp: prop => prop !== 'hasFunctionNameHiddenDetails',
  148. })<{
  149. hasFunctionNameHiddenDetails: boolean;
  150. }>`
  151. cursor: pointer;
  152. visibility: hidden;
  153. display: none;
  154. @media (min-width: ${p => p.theme.breakpoints.small}) {
  155. display: block;
  156. }
  157. ${p => !p.hasFunctionNameHiddenDetails && 'opacity: 0; cursor: inherit;'};
  158. `;
  159. const FunctionNameToggleTooltip = styled(Tooltip)`
  160. height: 16px;
  161. align-items: center;
  162. margin-right: ${space(0.75)};
  163. @media (max-width: ${p => p.theme.breakpoints.small}) {
  164. display: none;
  165. }
  166. `;
  167. export default Symbol;