index.tsx 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. import {useState} from 'react';
  2. import styled from '@emotion/styled';
  3. import classNames from 'classnames';
  4. import ListItem from 'sentry/components/list/listItem';
  5. import StrictClick from 'sentry/components/strictClick';
  6. import type {
  7. PlatformKey,
  8. SentryAppComponent,
  9. SentryAppSchemaStacktraceLink,
  10. } from 'sentry/types';
  11. import type {Event} from 'sentry/types/event';
  12. import withSentryAppComponents from 'sentry/utils/withSentryAppComponents';
  13. import Context from '../context';
  14. import {PackageStatusIcon} from '../packageStatus';
  15. import {FunctionNameToggleIcon} from '../symbol';
  16. import {AddressToggleIcon} from '../togglableAddress';
  17. import {
  18. getPlatform,
  19. hasAssembly,
  20. hasContextRegisters,
  21. hasContextSource,
  22. hasContextVars,
  23. isExpandable,
  24. } from '../utils';
  25. import Default from './default';
  26. import {Native} from './native';
  27. type Props = Omit<
  28. React.ComponentProps<typeof Native>,
  29. 'onToggleContext' | 'isExpandable' | 'leadsToApp' | 'hasGroupingBadge'
  30. > &
  31. Omit<
  32. React.ComponentProps<typeof Default>,
  33. 'onToggleContext' | 'isExpandable' | 'leadsToApp' | 'hasGroupingBadge'
  34. > & {
  35. components: SentryAppComponent<SentryAppSchemaStacktraceLink>[];
  36. event: Event;
  37. registers: Record<string, string>;
  38. emptySourceNotation?: boolean;
  39. frameMeta?: Record<string, any>;
  40. isOnlyFrame?: boolean;
  41. registersMeta?: Record<string, any>;
  42. };
  43. function Line({
  44. frame,
  45. nextFrame,
  46. prevFrame,
  47. timesRepeated,
  48. includeSystemFrames,
  49. isFrameAfterLastNonApp,
  50. isUsedForGrouping,
  51. maxLengthOfRelativeAddress,
  52. image,
  53. registers,
  54. isOnlyFrame,
  55. event,
  56. components,
  57. frameMeta,
  58. registersMeta,
  59. emptySourceNotation = false,
  60. /**
  61. * Is the stack trace being previewed in a hovercard?
  62. */
  63. isHoverPreviewed = false,
  64. ...props
  65. }: Props) {
  66. // Prioritize the frame platform but fall back to the platform
  67. // of the stack trace / exception
  68. const platform = getPlatform(frame.platform, props.platform ?? 'other') as PlatformKey;
  69. const leadsToApp = !frame.inApp && (nextFrame?.inApp || !nextFrame);
  70. const expandable =
  71. !leadsToApp || includeSystemFrames
  72. ? isExpandable({
  73. frame,
  74. registers,
  75. platform,
  76. emptySourceNotation,
  77. isOnlyFrame,
  78. })
  79. : false;
  80. const [isExpanded, setIsExpanded] = useState(
  81. expandable ? props.isExpanded ?? false : false
  82. );
  83. function toggleContext(evt: React.MouseEvent) {
  84. evt.preventDefault();
  85. setIsExpanded(!isExpanded);
  86. }
  87. function renderLine() {
  88. switch (platform) {
  89. case 'objc':
  90. case 'cocoa':
  91. case 'native':
  92. case 'nintendo-switch':
  93. return (
  94. <Native
  95. event={event}
  96. leadsToApp={leadsToApp}
  97. frame={frame}
  98. prevFrame={prevFrame}
  99. nextFrame={nextFrame}
  100. isHoverPreviewed={isHoverPreviewed}
  101. platform={platform}
  102. isExpanded={isExpanded}
  103. isExpandable={expandable}
  104. includeSystemFrames={includeSystemFrames}
  105. isFrameAfterLastNonApp={isFrameAfterLastNonApp}
  106. onToggleContext={toggleContext}
  107. image={image}
  108. maxLengthOfRelativeAddress={maxLengthOfRelativeAddress}
  109. isUsedForGrouping={isUsedForGrouping}
  110. />
  111. );
  112. default:
  113. return (
  114. <Default
  115. event={event}
  116. leadsToApp={leadsToApp}
  117. frame={frame}
  118. nextFrame={nextFrame}
  119. timesRepeated={timesRepeated}
  120. isHoverPreviewed={isHoverPreviewed}
  121. platform={platform}
  122. isExpanded={isExpanded}
  123. isExpandable={expandable}
  124. onToggleContext={toggleContext}
  125. isUsedForGrouping={isUsedForGrouping}
  126. frameMeta={frameMeta}
  127. />
  128. );
  129. }
  130. }
  131. const className = classNames({
  132. frame: true,
  133. 'is-expandable': expandable,
  134. expanded: isExpanded,
  135. collapsed: !isExpanded,
  136. 'system-frame': !frame.inApp,
  137. 'leads-to-app': leadsToApp,
  138. });
  139. return (
  140. <StyleListItem className={className} data-test-id="stack-trace-frame">
  141. <StrictClick onClick={expandable ? toggleContext : undefined}>
  142. {renderLine()}
  143. </StrictClick>
  144. <Context
  145. frame={frame}
  146. event={event}
  147. registers={registers}
  148. components={components}
  149. hasContextSource={hasContextSource(frame)}
  150. hasContextVars={hasContextVars(frame)}
  151. hasContextRegisters={hasContextRegisters(registers)}
  152. emptySourceNotation={emptySourceNotation}
  153. hasAssembly={hasAssembly(frame, platform)}
  154. isExpanded={isExpanded}
  155. registersMeta={registersMeta}
  156. frameMeta={frameMeta}
  157. platform={platform}
  158. />
  159. </StyleListItem>
  160. );
  161. }
  162. export default withSentryAppComponents(Line, {componentType: 'stacktrace-link'});
  163. const StyleListItem = styled(ListItem)`
  164. overflow: hidden;
  165. :first-child {
  166. border-top: none;
  167. }
  168. ${PackageStatusIcon} {
  169. flex-shrink: 0;
  170. }
  171. :hover {
  172. ${PackageStatusIcon} {
  173. visibility: visible;
  174. }
  175. ${AddressToggleIcon} {
  176. visibility: visible;
  177. }
  178. ${FunctionNameToggleIcon} {
  179. visibility: visible;
  180. }
  181. }
  182. `;