mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Migrate log entry component to new PDF preview (#5478)
* Migrate log entry component to new PDF preview * Add a test for expandable log content GitOrigin-RevId: 3e2154983c1ea03b5db44c87822e7043c4aa2cfe
This commit is contained in:
parent
f7ef2532e0
commit
12eab99990
5 changed files with 211 additions and 3 deletions
|
@ -0,0 +1,37 @@
|
|||
import { useTranslation } from 'react-i18next'
|
||||
import PdfLogEntryRawContent from './pdf-log-entry-raw-content'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
export default function PdfLogEntryContent({
|
||||
rawContent,
|
||||
formattedContent,
|
||||
extraInfoURL,
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<div className="log-entry-content">
|
||||
{formattedContent && (
|
||||
<div className="log-entry-formatted-content">{formattedContent}</div>
|
||||
)}
|
||||
|
||||
{extraInfoURL && (
|
||||
<div className="log-entry-content-link">
|
||||
<a href={extraInfoURL} target="_blank" rel="noopener">
|
||||
{t('log_hint_extra_info')}
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{rawContent && (
|
||||
<PdfLogEntryRawContent rawContent={rawContent} collapsedSize={150} />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
PdfLogEntryContent.propTypes = {
|
||||
rawContent: PropTypes.string,
|
||||
formattedContent: PropTypes.node,
|
||||
extraInfoURL: PropTypes.string,
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
import { useState } from 'react'
|
||||
import { useResizeObserver } from '../hooks/use-resize-observer'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import classNames from 'classnames'
|
||||
import { Button } from 'react-bootstrap'
|
||||
import Icon from '../../../shared/components/icon'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
export default function PdfLogEntryRawContent({
|
||||
rawContent,
|
||||
collapsedSize = 0,
|
||||
}) {
|
||||
const [expanded, setExpanded] = useState(false)
|
||||
const [needsExpander, setNeedsExpander] = useState(false)
|
||||
|
||||
const containerRef = useResizeObserver(entry => {
|
||||
setNeedsExpander(entry.target.scrollHeight > collapsedSize)
|
||||
})
|
||||
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<div className="log-entry-content-raw-container">
|
||||
<div
|
||||
className="expand-collapse-container"
|
||||
style={{
|
||||
height: expanded ? 'auto' : collapsedSize,
|
||||
}}
|
||||
>
|
||||
<pre className="log-entry-content-raw" ref={containerRef}>
|
||||
{rawContent.trim()}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
{needsExpander && (
|
||||
<div
|
||||
className={classNames('log-entry-content-button-container', {
|
||||
'log-entry-content-button-container-collapsed': !expanded,
|
||||
})}
|
||||
>
|
||||
<Button
|
||||
bsSize="xs"
|
||||
bsStyle="default"
|
||||
className="log-entry-btn-expand-collapse"
|
||||
onClick={() => setExpanded(value => !value)}
|
||||
>
|
||||
{expanded ? (
|
||||
<>
|
||||
<Icon type="angle-up" /> {t('collapse')}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Icon type="angle-down" /> {t('expand')}
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
PdfLogEntryRawContent.propTypes = {
|
||||
rawContent: PropTypes.string.isRequired,
|
||||
collapsedSize: PropTypes.number,
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
import PropTypes from 'prop-types'
|
||||
import classNames from 'classnames'
|
||||
import { memo, useCallback } from 'react'
|
||||
import PreviewLogEntryHeader from '../../preview/components/preview-log-entry-header'
|
||||
import PdfLogEntryContent from './pdf-log-entry-content'
|
||||
|
||||
function PdfLogEntry({
|
||||
headerTitle,
|
||||
headerIcon,
|
||||
rawContent,
|
||||
logType,
|
||||
formattedContent,
|
||||
extraInfoURL,
|
||||
level,
|
||||
sourceLocation,
|
||||
showSourceLocationLink = true,
|
||||
showCloseButton = false,
|
||||
entryAriaLabel = null,
|
||||
customClass,
|
||||
onSourceLocationClick,
|
||||
onClose,
|
||||
}) {
|
||||
const handleLogEntryLinkClick = useCallback(
|
||||
event => {
|
||||
event.preventDefault()
|
||||
onSourceLocationClick(sourceLocation)
|
||||
},
|
||||
[onSourceLocationClick, sourceLocation]
|
||||
)
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames('log-entry', customClass)}
|
||||
aria-label={entryAriaLabel}
|
||||
>
|
||||
<PreviewLogEntryHeader
|
||||
level={level}
|
||||
sourceLocation={sourceLocation}
|
||||
headerTitle={headerTitle}
|
||||
headerIcon={headerIcon}
|
||||
logType={logType}
|
||||
showSourceLocationLink={showSourceLocationLink}
|
||||
onSourceLocationClick={handleLogEntryLinkClick}
|
||||
showCloseButton={showCloseButton}
|
||||
onClose={onClose}
|
||||
/>
|
||||
{(rawContent || formattedContent) && (
|
||||
<PdfLogEntryContent
|
||||
rawContent={rawContent}
|
||||
formattedContent={formattedContent}
|
||||
extraInfoURL={extraInfoURL}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
PdfLogEntry.propTypes = {
|
||||
sourceLocation: PreviewLogEntryHeader.propTypes.sourceLocation,
|
||||
headerTitle: PropTypes.string,
|
||||
headerIcon: PropTypes.element,
|
||||
rawContent: PropTypes.string,
|
||||
logType: PropTypes.string,
|
||||
formattedContent: PropTypes.node,
|
||||
extraInfoURL: PropTypes.string,
|
||||
level: PropTypes.oneOf(['error', 'warning', 'typesetting', 'raw', 'success'])
|
||||
.isRequired,
|
||||
customClass: PropTypes.string,
|
||||
showSourceLocationLink: PropTypes.bool,
|
||||
showCloseButton: PropTypes.bool,
|
||||
entryAriaLabel: PropTypes.string,
|
||||
onSourceLocationClick: PropTypes.func,
|
||||
onClose: PropTypes.func,
|
||||
}
|
||||
|
||||
export default memo(PdfLogEntry)
|
|
@ -1,8 +1,8 @@
|
|||
import { memo, useCallback } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import PreviewLogsPaneEntry from '../../preview/components/preview-logs-pane-entry'
|
||||
import PreviewLogsPaneMaxEntries from '../../preview/components/preview-logs-pane-max-entries'
|
||||
import PdfLogEntry from './pdf-log-entry'
|
||||
|
||||
const LOG_PREVIEW_LIMIT = 100
|
||||
|
||||
|
@ -23,13 +23,12 @@ function PdfLogsEntries({ entries }) {
|
|||
<>
|
||||
{entries.length > LOG_PREVIEW_LIMIT && (
|
||||
<PreviewLogsPaneMaxEntries
|
||||
key={`${entries.length}-${LOG_PREVIEW_LIMIT}`}
|
||||
totalEntries={entries.length}
|
||||
entriesShown={LOG_PREVIEW_LIMIT}
|
||||
/>
|
||||
)}
|
||||
{logEntries.map(logEntry => (
|
||||
<PreviewLogsPaneEntry
|
||||
<PdfLogEntry
|
||||
key={logEntry.key}
|
||||
headerTitle={logEntry.message}
|
||||
rawContent={logEntry.content}
|
||||
|
|
|
@ -148,6 +148,7 @@ describe('<PdfPreview/>', function () {
|
|||
// xhrMock.teardown()
|
||||
fetchMock.reset()
|
||||
localStorage.clear()
|
||||
sinon.restore()
|
||||
})
|
||||
|
||||
it('renders the PDF preview', async function () {
|
||||
|
@ -270,6 +271,35 @@ describe('<PdfPreview/>', function () {
|
|||
expect(fetchMock.called('express:/build/:file')).to.be.false // TODO: actual path
|
||||
})
|
||||
|
||||
it('displays expandable raw logs', async function () {
|
||||
mockCompile()
|
||||
mockBuildFile()
|
||||
|
||||
// pretend that the content is large enough to trigger a "collapse"
|
||||
// (in jsdom these values are always zero)
|
||||
sinon.stub(HTMLElement.prototype, 'scrollHeight').value(500)
|
||||
sinon.stub(HTMLElement.prototype, 'scrollWidth').value(500)
|
||||
|
||||
renderWithEditorContext(<PdfPreview />, { scope })
|
||||
|
||||
// wait for "compile on load" to finish
|
||||
await screen.findByRole('button', { name: 'Compiling…' })
|
||||
await screen.findByRole('button', { name: 'Recompile' })
|
||||
|
||||
const logsButton = screen.getByRole('button', { name: 'View logs' })
|
||||
logsButton.click()
|
||||
|
||||
await screen.findByRole('button', { name: 'View PDF' })
|
||||
|
||||
// expand the log
|
||||
const expandButton = screen.getByRole('button', { name: 'Expand' })
|
||||
expandButton.click()
|
||||
|
||||
// collapse the log
|
||||
const collapseButton = screen.getByRole('button', { name: 'Collapse' })
|
||||
collapseButton.click()
|
||||
})
|
||||
|
||||
it('displays error messages if there were validation problems', async function () {
|
||||
const validationProblems = {
|
||||
sizeCheck: {
|
||||
|
|
Loading…
Reference in a new issue