contextLine.tsx 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. import styled from '@emotion/styled';
  2. import classNames from 'classnames';
  3. import {Tooltip} from 'sentry/components/tooltip';
  4. import {t} from 'sentry/locale';
  5. import {space} from 'sentry/styles/space';
  6. import {Coverage} from 'sentry/types';
  7. interface Props {
  8. isActive: boolean;
  9. line: [lineNo: number, content: string];
  10. children?: React.ReactNode;
  11. coverage?: Coverage | '';
  12. }
  13. const coverageText: Record<Coverage, string | undefined> = {
  14. [Coverage.NOT_COVERED]: t('Uncovered'),
  15. [Coverage.COVERED]: t('Covered'),
  16. [Coverage.PARTIAL]: t('Partially Covered'),
  17. [Coverage.NOT_APPLICABLE]: undefined,
  18. };
  19. const coverageClass: Record<Coverage, string | undefined> = {
  20. [Coverage.NOT_COVERED]: 'uncovered',
  21. [Coverage.COVERED]: 'covered',
  22. [Coverage.PARTIAL]: 'partial',
  23. [Coverage.NOT_APPLICABLE]: undefined,
  24. };
  25. function ContextLine({line, isActive, children, coverage = ''}: Props) {
  26. let lineWs = '';
  27. let lineCode = '';
  28. if (typeof line[1] === 'string') {
  29. [, lineWs, lineCode] = line[1].match(/^(\s*)(.*?)$/m)!;
  30. }
  31. return (
  32. <StyledLi
  33. className={classNames(
  34. 'expandable',
  35. coverageClass[coverage],
  36. isActive ? 'active' : ''
  37. )}
  38. >
  39. <LineContent>
  40. <Tooltip skipWrapper title={coverageText[coverage]} delay={200}>
  41. <div className="line-number">{line[0]}</div>
  42. </Tooltip>
  43. <div>
  44. <span className="ws">{lineWs}</span>
  45. <span className="contextline">{lineCode}</span>
  46. </div>
  47. </LineContent>
  48. {children}
  49. </StyledLi>
  50. );
  51. }
  52. export default ContextLine;
  53. const StyledLi = styled('li')`
  54. background: inherit;
  55. z-index: 1000;
  56. list-style: none;
  57. &::marker {
  58. content: none;
  59. }
  60. .line-number {
  61. display: flex;
  62. align-items: center;
  63. flex-direction: row;
  64. flex-wrap: nowrap;
  65. justify-content: end;
  66. height: 100%;
  67. text-align: right;
  68. padding-left: ${space(2)};
  69. padding-right: ${space(2)};
  70. margin-right: ${space(1.5)};
  71. background: transparent;
  72. z-index: 1;
  73. min-width: 58px;
  74. border-right-style: solid;
  75. border-right-color: transparent;
  76. user-select: none;
  77. }
  78. &.covered .line-number {
  79. background: ${p => p.theme.green100};
  80. }
  81. &.uncovered .line-number {
  82. background: ${p => p.theme.red100};
  83. border-right-color: ${p => p.theme.red300};
  84. }
  85. &.partial .line-number {
  86. background: ${p => p.theme.yellow100};
  87. border-right-style: dashed;
  88. border-right-color: ${p => p.theme.yellow300};
  89. }
  90. &.active {
  91. background: ${p => p.theme.stacktraceActiveBackground};
  92. color: ${p => p.theme.stacktraceActiveText};
  93. }
  94. &.active.partial .line-number {
  95. mix-blend-mode: screen;
  96. background: ${p => p.theme.yellow200};
  97. }
  98. &.active.covered .line-number {
  99. mix-blend-mode: screen;
  100. background: ${p => p.theme.green200};
  101. }
  102. &.active.uncovered .line-number {
  103. mix-blend-mode: screen;
  104. background: ${p => p.theme.red300};
  105. }
  106. `;
  107. // TODO(scttcper): The parent component should be a grid, currently has too many other children
  108. // If the grid was higher up we wouldn't have to hardcode the line number width and could fit larger line numbers
  109. const lineNumberWidth = '58px';
  110. const padding = space(2);
  111. const LineContent = styled('div')`
  112. display: grid;
  113. grid-template-columns: ${lineNumberWidth} calc(100% - ${lineNumberWidth} - ${padding});
  114. gap: ${space(0.5)};
  115. `;