log-entry.tsx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. import {
  2. Dispatch,
  3. MouseEventHandler,
  4. useCallback,
  5. memo,
  6. SetStateAction,
  7. useState,
  8. } from 'react'
  9. import HumanReadableLogsHints from '../../../../ide/human-readable-logs/HumanReadableLogsHints'
  10. import { sendMB } from '@/infrastructure/event-tracking'
  11. import {
  12. ErrorLevel,
  13. LogEntry as LogEntryData,
  14. SourceLocation,
  15. } from '@/features/pdf-preview/util/types'
  16. import LogEntryHeader from './log-entry-header'
  17. import PdfLogEntryContent from '@/features/pdf-preview/components/pdf-log-entry-content'
  18. import classNames from 'classnames'
  19. type LogEntryProps = {
  20. headerTitle: string | React.ReactNode
  21. level: ErrorLevel
  22. ruleId?: string
  23. rawContent?: string
  24. logType?: string
  25. formattedContent?: React.ReactNode
  26. extraInfoURL?: string | null
  27. sourceLocation?: SourceLocation
  28. showSourceLocationLink?: boolean
  29. entryAriaLabel?: string
  30. contentDetails?: string[]
  31. onSourceLocationClick?: (sourceLocation: SourceLocation) => void
  32. index?: number
  33. logEntry?: LogEntryData
  34. id?: string
  35. alwaysExpandRawContent?: boolean
  36. className?: string
  37. actionButtonsOverride?: React.ReactNode
  38. openCollapseIconOverride?: string
  39. }
  40. function LogEntry(props: LogEntryProps) {
  41. const [collapsed, setCollapsed] = useState(props.index !== 0)
  42. return (
  43. <ControlledLogEntry
  44. {...props}
  45. collapsed={collapsed}
  46. setCollapsed={setCollapsed}
  47. />
  48. )
  49. }
  50. export function ControlledLogEntry({
  51. ruleId,
  52. headerTitle,
  53. rawContent,
  54. logType,
  55. formattedContent,
  56. extraInfoURL,
  57. level,
  58. sourceLocation,
  59. showSourceLocationLink = true,
  60. entryAriaLabel = undefined,
  61. contentDetails,
  62. onSourceLocationClick,
  63. index,
  64. logEntry,
  65. id,
  66. alwaysExpandRawContent = false,
  67. className,
  68. collapsed,
  69. setCollapsed,
  70. actionButtonsOverride,
  71. openCollapseIconOverride,
  72. }: LogEntryProps & {
  73. collapsed: boolean
  74. setCollapsed: Dispatch<SetStateAction<boolean>>
  75. }) {
  76. if (ruleId && HumanReadableLogsHints[ruleId]) {
  77. const hint = HumanReadableLogsHints[ruleId]
  78. formattedContent = hint.formattedContent(contentDetails)
  79. extraInfoURL = hint.extraInfoURL
  80. }
  81. const handleLogEntryLinkClick: MouseEventHandler<HTMLButtonElement> =
  82. useCallback(
  83. event => {
  84. event.preventDefault()
  85. if (onSourceLocationClick && sourceLocation) {
  86. onSourceLocationClick(sourceLocation)
  87. const parts = sourceLocation?.file?.split('.')
  88. const extension =
  89. parts?.length && parts?.length > 1 ? parts.pop() : ''
  90. sendMB('log-entry-link-click', { level, ruleId, extension })
  91. }
  92. },
  93. [level, onSourceLocationClick, ruleId, sourceLocation]
  94. )
  95. return (
  96. <div
  97. className={classNames('log-entry', className)}
  98. data-ruleid={ruleId}
  99. data-log-entry-id={id}
  100. aria-label={entryAriaLabel}
  101. >
  102. <LogEntryHeader
  103. level={level}
  104. sourceLocation={sourceLocation}
  105. headerTitle={headerTitle}
  106. logType={logType}
  107. showSourceLocationLink={showSourceLocationLink}
  108. onSourceLocationClick={handleLogEntryLinkClick}
  109. collapsed={collapsed}
  110. onToggleCollapsed={() => setCollapsed(collapsed => !collapsed)}
  111. id={id}
  112. logEntry={logEntry}
  113. actionButtonsOverride={actionButtonsOverride}
  114. openCollapseIconOverride={openCollapseIconOverride}
  115. />
  116. <div
  117. className={classNames('horizontal-divider', { hidden: collapsed })}
  118. />
  119. <PdfLogEntryContent
  120. className={classNames({ hidden: collapsed })}
  121. alwaysExpandRawContent={alwaysExpandRawContent}
  122. rawContent={rawContent}
  123. formattedContent={formattedContent}
  124. extraInfoURL={extraInfoURL}
  125. index={index}
  126. logEntry={logEntry}
  127. />
  128. </div>
  129. )
  130. }
  131. export default memo(LogEntry)