mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #3330 from overleaf/jel-toolbar-btns
Hide toolbar text and show tooltip when out of space GitOrigin-RevId: 5a73b69e7d92695c4f8691a747307908550e3790
This commit is contained in:
parent
da8663fd0f
commit
21ffe27bdd
10 changed files with 593 additions and 172 deletions
|
@ -1,12 +1,17 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { Dropdown, MenuItem } from 'react-bootstrap'
|
import { Dropdown, MenuItem, OverlayTrigger, Tooltip } from 'react-bootstrap'
|
||||||
import { useTranslation, Trans } from 'react-i18next'
|
import { useTranslation, Trans } from 'react-i18next'
|
||||||
import Icon from '../../../shared/components/icon'
|
import Icon from '../../../shared/components/icon'
|
||||||
|
|
||||||
export const topFileTypes = ['bbl', 'gls', 'ind']
|
export const topFileTypes = ['bbl', 'gls', 'ind']
|
||||||
|
|
||||||
function PreviewDownloadButton({ isCompiling, outputFiles, pdfDownloadUrl }) {
|
function PreviewDownloadButton({
|
||||||
|
isCompiling,
|
||||||
|
outputFiles,
|
||||||
|
pdfDownloadUrl,
|
||||||
|
showText
|
||||||
|
}) {
|
||||||
let topFiles = []
|
let topFiles = []
|
||||||
let otherFiles = []
|
let otherFiles = []
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
@ -26,16 +31,46 @@ function PreviewDownloadButton({ isCompiling, outputFiles, pdfDownloadUrl }) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let textStyle = {}
|
||||||
|
if (!showText) {
|
||||||
|
textStyle = {
|
||||||
|
position: 'absolute',
|
||||||
|
right: '-100vw'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const buttonElement = (
|
||||||
|
<a
|
||||||
|
className="btn btn-xs btn-info"
|
||||||
|
disabled={isCompiling || !pdfDownloadUrl}
|
||||||
|
download
|
||||||
|
href={pdfDownloadUrl || '#'}
|
||||||
|
>
|
||||||
|
<Icon type="download" modifier="fw" />
|
||||||
|
<span className="toolbar-text" style={textStyle}>
|
||||||
|
{t('download_pdf')}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dropdown id="download-dropdown" disabled={isCompiling}>
|
<Dropdown
|
||||||
<a
|
id="download-dropdown"
|
||||||
className="btn btn-xs btn-info"
|
className="toolbar-item"
|
||||||
disabled={isCompiling || !pdfDownloadUrl}
|
disabled={isCompiling}
|
||||||
download
|
>
|
||||||
href={pdfDownloadUrl || '#'}
|
{showText ? (
|
||||||
>
|
buttonElement
|
||||||
<Icon type="download" modifier="fw" /> {t('download_pdf')}
|
) : (
|
||||||
</a>
|
<OverlayTrigger
|
||||||
|
placement="bottom"
|
||||||
|
overlay={
|
||||||
|
<Tooltip id="tooltip-download-pdf">{t('download_pdf')}</Tooltip>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{buttonElement}
|
||||||
|
</OverlayTrigger>
|
||||||
|
)}
|
||||||
<Dropdown.Toggle
|
<Dropdown.Toggle
|
||||||
className="btn btn-xs btn-info dropdown-toggle"
|
className="btn btn-xs btn-info dropdown-toggle"
|
||||||
aria-label={t('toggle_output_files_list')}
|
aria-label={t('toggle_output_files_list')}
|
||||||
|
@ -79,7 +114,8 @@ function FileList({ listType, list }) {
|
||||||
PreviewDownloadButton.propTypes = {
|
PreviewDownloadButton.propTypes = {
|
||||||
isCompiling: PropTypes.bool.isRequired,
|
isCompiling: PropTypes.bool.isRequired,
|
||||||
outputFiles: PropTypes.array,
|
outputFiles: PropTypes.array,
|
||||||
pdfDownloadUrl: PropTypes.string
|
pdfDownloadUrl: PropTypes.string,
|
||||||
|
showText: PropTypes.bool.isRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
FileList.propTypes = {
|
FileList.propTypes = {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
@ -7,77 +8,122 @@ import Icon from '../../../shared/components/icon'
|
||||||
function PreviewLogsToggleButton({
|
function PreviewLogsToggleButton({
|
||||||
onToggle,
|
onToggle,
|
||||||
showLogs,
|
showLogs,
|
||||||
logsState: { nErrors, nWarnings }
|
logsState: { nErrors, nWarnings },
|
||||||
|
showText
|
||||||
}) {
|
}) {
|
||||||
const toggleButtonClasses = classNames('btn', 'btn-xs', 'btn-toggle-logs', {
|
const { t } = useTranslation()
|
||||||
'btn-danger': !showLogs && nErrors,
|
const toggleButtonClasses = classNames(
|
||||||
'btn-warning': !showLogs && !nErrors && nWarnings,
|
'btn',
|
||||||
'btn-default': showLogs || (!nErrors && !nWarnings)
|
'btn-xs',
|
||||||
})
|
'btn-toggle-logs',
|
||||||
|
'toolbar-item',
|
||||||
|
{
|
||||||
|
'btn-danger': !showLogs && nErrors,
|
||||||
|
'btn-warning': !showLogs && !nErrors && nWarnings,
|
||||||
|
'btn-default': showLogs || (!nErrors && !nWarnings)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
let textStyle = {}
|
||||||
|
if (!showText) {
|
||||||
|
textStyle = {
|
||||||
|
position: 'absolute',
|
||||||
|
right: '-100vw'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function handleOnClick(e) {
|
function handleOnClick(e) {
|
||||||
e.currentTarget.blur()
|
e.currentTarget.blur()
|
||||||
onToggle()
|
onToggle()
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
const buttonElement = (
|
||||||
<button
|
<button
|
||||||
|
id="logs-toggle"
|
||||||
type="button"
|
type="button"
|
||||||
className={toggleButtonClasses}
|
className={toggleButtonClasses}
|
||||||
onClick={handleOnClick}
|
onClick={handleOnClick}
|
||||||
>
|
>
|
||||||
{showLogs ? (
|
{showLogs ? (
|
||||||
<ViewPdfButton />
|
<ViewPdfButton textStyle={textStyle} />
|
||||||
) : (
|
) : (
|
||||||
<CompilationResultIndicator nErrors={nErrors} nWarnings={nWarnings} />
|
<CompilationResultIndicator
|
||||||
|
textStyle={textStyle}
|
||||||
|
nErrors={nErrors}
|
||||||
|
nWarnings={nWarnings}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return showText ? (
|
||||||
|
buttonElement
|
||||||
|
) : (
|
||||||
|
<OverlayTrigger
|
||||||
|
placement="bottom"
|
||||||
|
overlay={
|
||||||
|
<Tooltip id="tooltip-logs-toggle">
|
||||||
|
{showLogs ? t('view_pdf') : t('view_logs')}
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{buttonElement}
|
||||||
|
</OverlayTrigger>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function CompilationResultIndicator({ nErrors, nWarnings }) {
|
function CompilationResultIndicator({ textStyle, nErrors, nWarnings }) {
|
||||||
if (nErrors || nWarnings) {
|
if (nErrors || nWarnings) {
|
||||||
return (
|
return (
|
||||||
<LogsCompilationResultIndicator
|
<LogsCompilationResultIndicator
|
||||||
logType={nErrors ? 'errors' : 'warnings'}
|
logType={nErrors ? 'errors' : 'warnings'}
|
||||||
nLogs={nErrors || nWarnings}
|
nLogs={nErrors || nWarnings}
|
||||||
|
textStyle={textStyle}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
return <ViewLogsButton />
|
return <ViewLogsButton textStyle={textStyle} />
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function LogsCompilationResultIndicator({ logType, nLogs }) {
|
function LogsCompilationResultIndicator({ textStyle, logType, nLogs }) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const label =
|
const label =
|
||||||
logType === 'errors' ? t('your_project_has_errors') : t('view_warnings')
|
logType === 'errors' ? t('your_project_has_errors') : t('view_warnings')
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Icon type="file-text-o" />
|
<Icon type="file-text-o" />
|
||||||
<span className="btn-toggle-logs-label" aria-label={label}>
|
<span
|
||||||
|
className="btn-toggle-logs-label toolbar-text"
|
||||||
|
aria-label={label}
|
||||||
|
style={textStyle}
|
||||||
|
>
|
||||||
{`${label} (${nLogs > 9 ? '9+' : nLogs})`}
|
{`${label} (${nLogs > 9 ? '9+' : nLogs})`}
|
||||||
</span>
|
</span>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function ViewLogsButton() {
|
function ViewLogsButton({ textStyle }) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Icon type="file-text-o" />
|
<Icon type="file-text-o" />
|
||||||
<span className="btn-toggle-logs-label">{t('view_logs')}</span>
|
<span className="toolbar-text" style={textStyle}>
|
||||||
|
{t('view_logs')}
|
||||||
|
</span>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function ViewPdfButton() {
|
function ViewPdfButton({ textStyle }) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Icon type="file-pdf-o" />
|
<Icon type="file-pdf-o" />
|
||||||
<span className="btn-toggle-logs-label">{t('view_pdf')}</span>
|
<span className="toolbar-text" style={textStyle}>
|
||||||
|
{t('view_pdf')}
|
||||||
|
</span>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -89,12 +135,22 @@ PreviewLogsToggleButton.propTypes = {
|
||||||
nWarnings: PropTypes.number.isRequired,
|
nWarnings: PropTypes.number.isRequired,
|
||||||
nLogEntries: PropTypes.number.isRequired
|
nLogEntries: PropTypes.number.isRequired
|
||||||
}),
|
}),
|
||||||
showLogs: PropTypes.bool.isRequired
|
showLogs: PropTypes.bool.isRequired,
|
||||||
|
showText: PropTypes.bool.isRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
LogsCompilationResultIndicator.propTypes = {
|
LogsCompilationResultIndicator.propTypes = {
|
||||||
logType: PropTypes.string.isRequired,
|
logType: PropTypes.string.isRequired,
|
||||||
nLogs: PropTypes.number.isRequired
|
nLogs: PropTypes.number.isRequired,
|
||||||
|
textStyle: PropTypes.object.isRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewLogsButton.propTypes = {
|
||||||
|
textStyle: PropTypes.object.isRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewPdfButton.propTypes = {
|
||||||
|
textStyle: PropTypes.object.isRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
export default PreviewLogsToggleButton
|
export default PreviewLogsToggleButton
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { Dropdown, MenuItem } from 'react-bootstrap'
|
import { Dropdown, MenuItem, OverlayTrigger, Tooltip } from 'react-bootstrap'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import Icon from '../../../shared/components/icon'
|
import Icon from '../../../shared/components/icon'
|
||||||
|
|
||||||
|
@ -17,7 +17,8 @@ function PreviewRecompileButton({
|
||||||
onRunSyntaxCheckNow,
|
onRunSyntaxCheckNow,
|
||||||
onSetAutoCompile,
|
onSetAutoCompile,
|
||||||
onSetDraftMode,
|
onSetDraftMode,
|
||||||
onSetSyntaxCheck
|
onSetSyntaxCheck,
|
||||||
|
showText
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
@ -55,18 +56,43 @@ function PreviewRecompileButton({
|
||||||
onSetSyntaxCheck(false)
|
onSetSyntaxCheck(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
let compilingProps = {}
|
||||||
<Dropdown id="pdf-recompile-dropdown" className="btn-recompile-group">
|
let recompileProps = {}
|
||||||
|
function _hideText(keepAria) {
|
||||||
|
return {
|
||||||
|
'aria-hidden': !keepAria,
|
||||||
|
style: {
|
||||||
|
position: 'absolute',
|
||||||
|
right: '-100vw'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!showText) {
|
||||||
|
compilingProps = _hideText(isCompiling || isClearingCache)
|
||||||
|
recompileProps = _hideText(!isCompiling || !isClearingCache)
|
||||||
|
} else if (isCompiling || isClearingCache) {
|
||||||
|
recompileProps = _hideText()
|
||||||
|
} else {
|
||||||
|
compilingProps = _hideText()
|
||||||
|
}
|
||||||
|
|
||||||
|
const buttonElement = (
|
||||||
|
<Dropdown
|
||||||
|
id="pdf-recompile-dropdown"
|
||||||
|
className="btn-recompile-group toolbar-item"
|
||||||
|
>
|
||||||
<button className="btn btn-recompile" onClick={onRecompile}>
|
<button className="btn btn-recompile" onClick={onRecompile}>
|
||||||
<Icon type="refresh" spin={isCompiling} />
|
<Icon type="refresh" spin={isCompiling} />
|
||||||
{isCompiling || isClearingCache ? (
|
|
||||||
<span className="btn-recompile-label">
|
<span id="text-compiling" className="toolbar-text" {...compilingProps}>
|
||||||
{t('compiling')}
|
{t('compiling')}
|
||||||
…
|
…
|
||||||
</span>
|
</span>
|
||||||
) : (
|
|
||||||
<span className="btn-recompile-label">{t('recompile')}</span>
|
<span id="text-recompile" className="toolbar-text" {...recompileProps}>
|
||||||
)}
|
{t('recompile')}
|
||||||
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<Dropdown.Toggle
|
<Dropdown.Toggle
|
||||||
aria-label={t('toggle_compile_options_menu')}
|
aria-label={t('toggle_compile_options_menu')}
|
||||||
|
@ -115,6 +141,21 @@ function PreviewRecompileButton({
|
||||||
</Dropdown.Menu>
|
</Dropdown.Menu>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return showText ? (
|
||||||
|
buttonElement
|
||||||
|
) : (
|
||||||
|
<OverlayTrigger
|
||||||
|
placement="bottom"
|
||||||
|
overlay={
|
||||||
|
<Tooltip id="tooltip-download-pdf">
|
||||||
|
{isCompiling || isClearingCache ? t('compiling') : t('recompile')}
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{buttonElement}
|
||||||
|
</OverlayTrigger>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
PreviewRecompileButton.propTypes = {
|
PreviewRecompileButton.propTypes = {
|
||||||
|
@ -131,7 +172,8 @@ PreviewRecompileButton.propTypes = {
|
||||||
onRunSyntaxCheckNow: PropTypes.func.isRequired,
|
onRunSyntaxCheckNow: PropTypes.func.isRequired,
|
||||||
onSetAutoCompile: PropTypes.func.isRequired,
|
onSetAutoCompile: PropTypes.func.isRequired,
|
||||||
onSetDraftMode: PropTypes.func.isRequired,
|
onSetDraftMode: PropTypes.func.isRequired,
|
||||||
onSetSyntaxCheck: PropTypes.func.isRequired
|
onSetSyntaxCheck: PropTypes.func.isRequired,
|
||||||
|
showText: PropTypes.bool.isRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
export default PreviewRecompileButton
|
export default PreviewRecompileButton
|
||||||
|
|
|
@ -1,8 +1,14 @@
|
||||||
import React from 'react'
|
import React, { useRef, useState } from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import PreviewDownloadButton from './preview-download-button'
|
import PreviewDownloadButton from './preview-download-button'
|
||||||
import PreviewRecompileButton from './preview-recompile-button'
|
import PreviewRecompileButton from './preview-recompile-button'
|
||||||
import PreviewLogsToggleButton from './preview-logs-toggle-button'
|
import PreviewLogsToggleButton from './preview-logs-toggle-button'
|
||||||
|
import useResizeObserver from '../../../shared/hooks/use-resize-observer'
|
||||||
|
|
||||||
|
function _getElementWidth(element) {
|
||||||
|
if (!element) return 0
|
||||||
|
return Math.ceil(element.getBoundingClientRect().width)
|
||||||
|
}
|
||||||
|
|
||||||
function PreviewToolbar({
|
function PreviewToolbar({
|
||||||
compilerState,
|
compilerState,
|
||||||
|
@ -18,8 +24,173 @@ function PreviewToolbar({
|
||||||
pdfDownloadUrl,
|
pdfDownloadUrl,
|
||||||
showLogs
|
showLogs
|
||||||
}) {
|
}) {
|
||||||
|
const showTextRef = useRef(true)
|
||||||
|
const showToggleTextRef = useRef(true)
|
||||||
|
const toolbarRef = useRef()
|
||||||
|
const recompileWidthDifferenceRef = useRef()
|
||||||
|
const recompileLongerTextRef = useRef()
|
||||||
|
const [showText, setShowText] = useState(showTextRef.current)
|
||||||
|
const [showToggleText, setShowToggleText] = useState(
|
||||||
|
showToggleTextRef.current
|
||||||
|
)
|
||||||
|
|
||||||
|
function checkCanShowText(observedElement) {
|
||||||
|
// toolbar items can be in 3 states:
|
||||||
|
// all text, only toggle logs w/text and icons on others, all icons
|
||||||
|
// states depend on available space in the toolbar
|
||||||
|
const toolbarWidth =
|
||||||
|
observedElement &&
|
||||||
|
observedElement.contentRect &&
|
||||||
|
observedElement.contentRect.width
|
||||||
|
if (!toolbarWidth) return
|
||||||
|
|
||||||
|
_checkRecompileStateWidths()
|
||||||
|
|
||||||
|
let textWidths = 0
|
||||||
|
let itemsWidth = _getItemsWidth() // could be with or without text
|
||||||
|
|
||||||
|
// get widths of text only
|
||||||
|
// required for _checkToggleText, even if currently showing text
|
||||||
|
const textElements = toolbarRef.current.querySelectorAll('.toolbar-text')
|
||||||
|
textElements.forEach(item => {
|
||||||
|
if (item.getAttribute('aria-hidden') !== 'true') {
|
||||||
|
textWidths += _getElementWidth(item)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const logsToggleText = toolbarRef.current.querySelector(
|
||||||
|
'#logs-toggle .toolbar-text'
|
||||||
|
)
|
||||||
|
const logsToggleTextWidth = _getElementWidth(logsToggleText)
|
||||||
|
|
||||||
|
if (!showTextRef.current && !showToggleTextRef.current) {
|
||||||
|
// itemsWidth was calculated without any text shown
|
||||||
|
itemsWidth += textWidths
|
||||||
|
} else if (!showTextRef.current && showToggleTextRef.current) {
|
||||||
|
// itemsWidth was calculated with toggle text but no other text
|
||||||
|
// only add text width for other items and then
|
||||||
|
// subtract toggle text width, since it is already in itemsWidth
|
||||||
|
itemsWidth += parseInt(textWidths - logsToggleTextWidth, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
// only add extra if recompile button is in state with smaller length
|
||||||
|
if (
|
||||||
|
recompileWidthDifferenceRef.current &&
|
||||||
|
recompileLongerTextRef.current &&
|
||||||
|
((!compilerState.isCompiling &&
|
||||||
|
recompileLongerTextRef.current === 'compiling') ||
|
||||||
|
(compilerState.isCompiling &&
|
||||||
|
recompileLongerTextRef.current === 'recompile'))
|
||||||
|
) {
|
||||||
|
itemsWidth += recompileWidthDifferenceRef.current
|
||||||
|
}
|
||||||
|
|
||||||
|
itemsWidth += 10 // add extra for some spacing between items
|
||||||
|
|
||||||
|
let canShowText = itemsWidth < toolbarWidth
|
||||||
|
|
||||||
|
if (!canShowText) {
|
||||||
|
_checkToggleText(
|
||||||
|
toolbarWidth,
|
||||||
|
logsToggleTextWidth,
|
||||||
|
itemsWidth,
|
||||||
|
textWidths
|
||||||
|
)
|
||||||
|
} else if (showToggleTextRef.current !== true) {
|
||||||
|
setShowToggleText(true)
|
||||||
|
showToggleTextRef.current = true
|
||||||
|
}
|
||||||
|
|
||||||
|
setShowText(canShowText)
|
||||||
|
showTextRef.current = canShowText
|
||||||
|
}
|
||||||
|
|
||||||
|
function _checkRecompileStateWidths() {
|
||||||
|
// check recompile/compiling button text.
|
||||||
|
// Do not want to hide and then show text when recompiling
|
||||||
|
if (
|
||||||
|
recompileWidthDifferenceRef.current ||
|
||||||
|
recompileWidthDifferenceRef.current === 0
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
const textCompiling = toolbarRef.current.querySelector('#text-compiling')
|
||||||
|
const textRecompile = toolbarRef.current.querySelector('#text-recompile')
|
||||||
|
|
||||||
|
const textCompilingWidth = _getElementWidth(textCompiling)
|
||||||
|
const textRecompileWidth = _getElementWidth(textRecompile)
|
||||||
|
|
||||||
|
const textLengthDifference = Math.abs(
|
||||||
|
parseInt(textCompilingWidth - textRecompileWidth, 10)
|
||||||
|
)
|
||||||
|
recompileWidthDifferenceRef.current = textLengthDifference
|
||||||
|
// ignore if equal
|
||||||
|
if (textRecompileWidth > textCompilingWidth) {
|
||||||
|
recompileLongerTextRef.current = 'recompile'
|
||||||
|
} else if (textRecompileWidth < textCompilingWidth) {
|
||||||
|
recompileLongerTextRef.current = 'compiling'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _checkToggleText(
|
||||||
|
toolbarWidth,
|
||||||
|
logsToggleTextWidth,
|
||||||
|
itemsWithTextWidth,
|
||||||
|
textWidths
|
||||||
|
) {
|
||||||
|
// check to see if we can still show the toggle button text
|
||||||
|
let toggleWithTextWidth = 0
|
||||||
|
let toggleWithoutTextWidth = 0
|
||||||
|
const itemsWithoutTextWidth = parseInt(itemsWithTextWidth - textWidths, 10)
|
||||||
|
const logsToggle = toolbarRef.current.querySelector('#logs-toggle')
|
||||||
|
const logsToggleWidth = _getElementWidth(logsToggle)
|
||||||
|
|
||||||
|
// logsToggleWidth could be with or without text
|
||||||
|
if (showToggleTextRef.current) {
|
||||||
|
toggleWithTextWidth = logsToggleWidth
|
||||||
|
toggleWithoutTextWidth = parseInt(
|
||||||
|
logsToggleWidth - logsToggleTextWidth,
|
||||||
|
10
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
toggleWithTextWidth = parseInt(logsToggleWidth + logsToggleTextWidth, 10)
|
||||||
|
toggleWithoutTextWidth = logsToggleWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
const itemsWithoutTextAndToggleWidth = parseInt(
|
||||||
|
itemsWithoutTextWidth - toggleWithoutTextWidth,
|
||||||
|
10
|
||||||
|
)
|
||||||
|
const itemsWithIconsExceptToggleWidth = parseInt(
|
||||||
|
itemsWithoutTextAndToggleWidth + toggleWithTextWidth,
|
||||||
|
10
|
||||||
|
)
|
||||||
|
const canShowToggleText = itemsWithIconsExceptToggleWidth < toolbarWidth
|
||||||
|
|
||||||
|
if (canShowToggleText !== showToggleTextRef.current) {
|
||||||
|
setShowToggleText(canShowToggleText)
|
||||||
|
showToggleTextRef.current = canShowToggleText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _getItemsWidth() {
|
||||||
|
const toolbarItems = toolbarRef.current.querySelectorAll('.toolbar-item')
|
||||||
|
let itemWidth = 0
|
||||||
|
toolbarItems.forEach(item => {
|
||||||
|
itemWidth += _getElementWidth(item)
|
||||||
|
})
|
||||||
|
return itemWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
useResizeObserver(toolbarRef, logsState, checkCanShowText)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="toolbar toolbar-pdf">
|
<div
|
||||||
|
className="toolbar toolbar-pdf"
|
||||||
|
id="toolbar-preview"
|
||||||
|
data-testid="toolbar-preview"
|
||||||
|
ref={toolbarRef}
|
||||||
|
>
|
||||||
<div className="toolbar-pdf-left">
|
<div className="toolbar-pdf-left">
|
||||||
<PreviewRecompileButton
|
<PreviewRecompileButton
|
||||||
compilerState={compilerState}
|
compilerState={compilerState}
|
||||||
|
@ -29,11 +200,13 @@ function PreviewToolbar({
|
||||||
onSetDraftMode={onSetDraftMode}
|
onSetDraftMode={onSetDraftMode}
|
||||||
onSetSyntaxCheck={onSetSyntaxCheck}
|
onSetSyntaxCheck={onSetSyntaxCheck}
|
||||||
onClearCache={onClearCache}
|
onClearCache={onClearCache}
|
||||||
|
showText={showText}
|
||||||
/>
|
/>
|
||||||
<PreviewDownloadButton
|
<PreviewDownloadButton
|
||||||
isCompiling={compilerState.isCompiling}
|
isCompiling={compilerState.isCompiling}
|
||||||
outputFiles={outputFiles}
|
outputFiles={outputFiles}
|
||||||
pdfDownloadUrl={pdfDownloadUrl}
|
pdfDownloadUrl={pdfDownloadUrl}
|
||||||
|
showText={showText}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="toolbar-pdf-right">
|
<div className="toolbar-pdf-right">
|
||||||
|
@ -41,6 +214,7 @@ function PreviewToolbar({
|
||||||
logsState={logsState}
|
logsState={logsState}
|
||||||
showLogs={showLogs}
|
showLogs={showLogs}
|
||||||
onToggle={onToggleLogs}
|
onToggle={onToggleLogs}
|
||||||
|
showText={showToggleText}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
38
services/web/frontend/js/shared/hooks/use-resize-observer.js
Normal file
38
services/web/frontend/js/shared/hooks/use-resize-observer.js
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import { useLayoutEffect, useRef } from 'react'
|
||||||
|
|
||||||
|
function useResizeObserver(observedElement, observedData, callback) {
|
||||||
|
const resizeObserver = useRef()
|
||||||
|
|
||||||
|
function observe() {
|
||||||
|
resizeObserver.current = new ResizeObserver(function(elementsObserved) {
|
||||||
|
callback(elementsObserved[0])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function unobserve(observedCurrent) {
|
||||||
|
resizeObserver.current.unobserve(observedCurrent)
|
||||||
|
}
|
||||||
|
|
||||||
|
useLayoutEffect(
|
||||||
|
() => {
|
||||||
|
if ('ResizeObserver' in window) {
|
||||||
|
const observedCurrent = observedElement && observedElement.current
|
||||||
|
|
||||||
|
if (observedCurrent) {
|
||||||
|
observe(observedElement.current)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resizeObserver.current && observedCurrent) {
|
||||||
|
resizeObserver.current.observe(observedCurrent)
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
unobserve(observedCurrent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[observedElement, observedData]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useResizeObserver
|
|
@ -103,6 +103,10 @@
|
||||||
margin-left: @line-height-computed / 4;
|
margin-left: @line-height-computed / 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.toolbar-text {
|
||||||
|
padding-left: @padding-xs;
|
||||||
|
}
|
||||||
|
|
||||||
.pdf-viewer {
|
.pdf-viewer {
|
||||||
iframe {
|
iframe {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -19,17 +19,36 @@ describe('<PreviewDownloadButton />', function() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should disable the button and dropdown toggle when compiling', function() {
|
function renderPreviewDownloadButton(
|
||||||
const isCompiling = true
|
isCompiling,
|
||||||
const outputFiles = undefined
|
outputFiles,
|
||||||
|
pdfDownloadUrl,
|
||||||
|
showText
|
||||||
|
) {
|
||||||
|
if (isCompiling === undefined) isCompiling = false
|
||||||
|
if (showText === undefined) showText = true
|
||||||
render(
|
render(
|
||||||
<PreviewDownloadButton
|
<PreviewDownloadButton
|
||||||
isCompiling={isCompiling}
|
isCompiling={isCompiling}
|
||||||
outputFiles={outputFiles}
|
outputFiles={outputFiles || []}
|
||||||
pdfDownloadUrl={undefined}
|
pdfDownloadUrl={pdfDownloadUrl}
|
||||||
|
showText={showText}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
expect(screen.getByText('Download PDF').getAttribute('disabled')).to.exist
|
}
|
||||||
|
|
||||||
|
it('should disable the button and dropdown toggle when compiling', function() {
|
||||||
|
const isCompiling = true
|
||||||
|
const outputFiles = undefined
|
||||||
|
|
||||||
|
renderPreviewDownloadButton(isCompiling, outputFiles)
|
||||||
|
|
||||||
|
expect(
|
||||||
|
screen
|
||||||
|
.getByText('Download PDF')
|
||||||
|
.closest('a')
|
||||||
|
.getAttribute('disabled')
|
||||||
|
).to.exist
|
||||||
const buttons = screen.getAllByRole('button')
|
const buttons = screen.getAllByRole('button')
|
||||||
expect(buttons.length).to.equal(1) // the dropdown toggle
|
expect(buttons.length).to.equal(1) // the dropdown toggle
|
||||||
expect(buttons[0].getAttribute('disabled')).to.exist
|
expect(buttons[0].getAttribute('disabled')).to.exist
|
||||||
|
@ -40,41 +59,35 @@ describe('<PreviewDownloadButton />', function() {
|
||||||
it('should disable the PDF button when there is no PDF', function() {
|
it('should disable the PDF button when there is no PDF', function() {
|
||||||
const isCompiling = false
|
const isCompiling = false
|
||||||
const outputFiles = []
|
const outputFiles = []
|
||||||
render(
|
renderPreviewDownloadButton(isCompiling, outputFiles)
|
||||||
<PreviewDownloadButton
|
expect(
|
||||||
isCompiling={isCompiling}
|
screen
|
||||||
outputFiles={outputFiles}
|
.getByText('Download PDF')
|
||||||
pdfDownloadUrl={undefined}
|
.closest('a')
|
||||||
/>
|
.getAttribute('disabled')
|
||||||
)
|
).to.exist
|
||||||
expect(screen.getByText('Download PDF').getAttribute('disabled')).to.exist
|
|
||||||
})
|
})
|
||||||
it('should enable the PDF button when there is a main PDF', function() {
|
it('should enable the PDF button when there is a main PDF', function() {
|
||||||
const isCompiling = false
|
const isCompiling = false
|
||||||
const outputFiles = []
|
const outputFiles = []
|
||||||
render(
|
renderPreviewDownloadButton(isCompiling, outputFiles, pdfDownloadUrl)
|
||||||
<PreviewDownloadButton
|
expect(
|
||||||
isCompiling={isCompiling}
|
screen
|
||||||
outputFiles={outputFiles}
|
.getByText('Download PDF')
|
||||||
pdfDownloadUrl={pdfDownloadUrl}
|
.closest('a')
|
||||||
/>
|
.getAttribute('href')
|
||||||
)
|
).to.equal(pdfDownloadUrl)
|
||||||
expect(screen.getByText('Download PDF').getAttribute('href')).to.equal(
|
expect(
|
||||||
pdfDownloadUrl
|
screen
|
||||||
)
|
.getByText('Download PDF')
|
||||||
expect(screen.getByText('Download PDF').getAttribute('disabled')).to.not
|
.closest('a')
|
||||||
.exist
|
.getAttribute('disabled')
|
||||||
|
).to.not.exist
|
||||||
})
|
})
|
||||||
it('should enable the dropdown when not compiling', function() {
|
it('should enable the dropdown when not compiling', function() {
|
||||||
const isCompiling = false
|
const isCompiling = false
|
||||||
const outputFiles = []
|
const outputFiles = []
|
||||||
render(
|
renderPreviewDownloadButton(isCompiling, outputFiles, pdfDownloadUrl)
|
||||||
<PreviewDownloadButton
|
|
||||||
isCompiling={isCompiling}
|
|
||||||
outputFiles={outputFiles}
|
|
||||||
pdfDownloadUrl={pdfDownloadUrl}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
const buttons = screen.getAllByRole('button')
|
const buttons = screen.getAllByRole('button')
|
||||||
expect(buttons[0]).to.exist
|
expect(buttons[0]).to.exist
|
||||||
expect(buttons[0].getAttribute('disabled')).to.not.exist
|
expect(buttons[0].getAttribute('disabled')).to.not.exist
|
||||||
|
@ -93,13 +106,7 @@ describe('<PreviewDownloadButton />', function() {
|
||||||
makeFile('output.blg')
|
makeFile('output.blg')
|
||||||
]
|
]
|
||||||
|
|
||||||
render(
|
renderPreviewDownloadButton(isCompiling, outputFiles, pdfDownloadUrl)
|
||||||
<PreviewDownloadButton
|
|
||||||
isCompiling={isCompiling}
|
|
||||||
outputFiles={outputFiles}
|
|
||||||
pdfDownloadUrl={pdfDownloadUrl}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
|
|
||||||
const menuItems = screen.getAllByRole('menuitem')
|
const menuItems = screen.getAllByRole('menuitem')
|
||||||
expect(menuItems.length).to.equal(outputFiles.length - 1) // main PDF is listed separately
|
expect(menuItems.length).to.equal(outputFiles.length - 1) // main PDF is listed separately
|
||||||
|
@ -138,13 +145,9 @@ describe('<PreviewDownloadButton />', function() {
|
||||||
const pdfFile = makeFile('output.pdf', true)
|
const pdfFile = makeFile('output.pdf', true)
|
||||||
const bblFile = makeFile('output.bbl')
|
const bblFile = makeFile('output.bbl')
|
||||||
const outputFiles = [Object.assign({}, { ...bblFile }), bblFile, pdfFile]
|
const outputFiles = [Object.assign({}, { ...bblFile }), bblFile, pdfFile]
|
||||||
render(
|
|
||||||
<PreviewDownloadButton
|
renderPreviewDownloadButton(isCompiling, outputFiles, pdfDownloadUrl)
|
||||||
isCompiling={isCompiling}
|
|
||||||
outputFiles={outputFiles}
|
|
||||||
pdfDownloadUrl={pdfDownloadUrl}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
const bblMenuItems = screen.getAllByText((content, element) => {
|
const bblMenuItems = screen.getAllByText((content, element) => {
|
||||||
return (
|
return (
|
||||||
content !== '' && element.textContent === 'Download output.bbl file'
|
content !== '' && element.textContent === 'Download output.bbl file'
|
||||||
|
@ -157,16 +160,23 @@ describe('<PreviewDownloadButton />', function() {
|
||||||
const pdfFile = makeFile('output.pdf', true)
|
const pdfFile = makeFile('output.pdf', true)
|
||||||
const pdfAltFile = makeFile('alt.pdf')
|
const pdfAltFile = makeFile('alt.pdf')
|
||||||
const outputFiles = [pdfFile, pdfAltFile]
|
const outputFiles = [pdfFile, pdfAltFile]
|
||||||
|
renderPreviewDownloadButton(isCompiling, outputFiles, pdfDownloadUrl)
|
||||||
render(
|
|
||||||
<PreviewDownloadButton
|
|
||||||
isCompiling={isCompiling}
|
|
||||||
outputFiles={outputFiles}
|
|
||||||
pdfDownloadUrl={pdfDownloadUrl}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
screen.getAllByRole('menuitem', { name: 'Download alt.pdf file' })
|
screen.getAllByRole('menuitem', { name: 'Download alt.pdf file' })
|
||||||
})
|
})
|
||||||
|
it('should show the button text when prop showText=true', function() {
|
||||||
|
const isCompiling = false
|
||||||
|
const showText = true
|
||||||
|
renderPreviewDownloadButton(isCompiling, [], pdfDownloadUrl, showText)
|
||||||
|
expect(screen.getByText('Download PDF').getAttribute('style')).to.be.null
|
||||||
|
})
|
||||||
|
it('should not show the button text when prop showText=false', function() {
|
||||||
|
const isCompiling = false
|
||||||
|
const showText = false
|
||||||
|
renderPreviewDownloadButton(isCompiling, [], pdfDownloadUrl, showText)
|
||||||
|
expect(screen.getByText('Download PDF').getAttribute('style')).to.equal(
|
||||||
|
'position: absolute; right: -100vw;'
|
||||||
|
)
|
||||||
|
})
|
||||||
describe('list divider and header', function() {
|
describe('list divider and header', function() {
|
||||||
it('should display when there are top files and other files', function() {
|
it('should display when there are top files and other files', function() {
|
||||||
const outputFiles = [
|
const outputFiles = [
|
||||||
|
@ -176,13 +186,7 @@ describe('<PreviewDownloadButton />', function() {
|
||||||
makeFile('output.log')
|
makeFile('output.log')
|
||||||
]
|
]
|
||||||
|
|
||||||
render(
|
renderPreviewDownloadButton(false, outputFiles, pdfDownloadUrl, true)
|
||||||
<PreviewDownloadButton
|
|
||||||
isCompiling={false}
|
|
||||||
outputFiles={outputFiles}
|
|
||||||
pdfDownloadUrl={pdfDownloadUrl}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
|
|
||||||
screen.getByText('Other output files')
|
screen.getByText('Other output files')
|
||||||
screen.getByRole('separator')
|
screen.getByRole('separator')
|
||||||
|
@ -194,13 +198,7 @@ describe('<PreviewDownloadButton />', function() {
|
||||||
makeFile('output.gls')
|
makeFile('output.gls')
|
||||||
]
|
]
|
||||||
|
|
||||||
render(
|
renderPreviewDownloadButton(false, outputFiles, pdfDownloadUrl, true)
|
||||||
<PreviewDownloadButton
|
|
||||||
isCompiling={false}
|
|
||||||
outputFiles={outputFiles}
|
|
||||||
pdfDownloadUrl={pdfDownloadUrl}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(screen.queryByText('Other output files')).to.not.exist
|
expect(screen.queryByText('Other output files')).to.not.exist
|
||||||
expect(screen.queryByRole('separator')).to.not.exist
|
expect(screen.queryByRole('separator')).to.not.exist
|
||||||
|
@ -208,13 +206,7 @@ describe('<PreviewDownloadButton />', function() {
|
||||||
it('should not display when there are other files and no top files', function() {
|
it('should not display when there are other files and no top files', function() {
|
||||||
const outputFiles = [makeFile('output.log')]
|
const outputFiles = [makeFile('output.log')]
|
||||||
|
|
||||||
render(
|
renderPreviewDownloadButton(false, outputFiles, pdfDownloadUrl, true)
|
||||||
<PreviewDownloadButton
|
|
||||||
isCompiling={false}
|
|
||||||
outputFiles={outputFiles}
|
|
||||||
pdfDownloadUrl={pdfDownloadUrl}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(screen.queryByText('Other output files')).to.not.exist
|
expect(screen.queryByText('Other output files')).to.not.exist
|
||||||
expect(screen.queryByRole('separator')).to.not.exist
|
expect(screen.queryByRole('separator')).to.not.exist
|
||||||
|
|
|
@ -1,9 +1,27 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import { expect } from 'chai'
|
||||||
import { screen, render } from '@testing-library/react'
|
import { screen, render } from '@testing-library/react'
|
||||||
|
|
||||||
import PreviewLogsToggleButton from '../../../../../frontend/js/features/preview/components/preview-logs-toggle-button'
|
import PreviewLogsToggleButton from '../../../../../frontend/js/features/preview/components/preview-logs-toggle-button'
|
||||||
|
|
||||||
describe('<PreviewLogsToggleButton />', function() {
|
describe('<PreviewLogsToggleButton />', function() {
|
||||||
|
function renderPreviewLogsToggleButton(
|
||||||
|
logsState,
|
||||||
|
onToggleLogs,
|
||||||
|
showLogs,
|
||||||
|
showText
|
||||||
|
) {
|
||||||
|
if (showText === undefined) showText = true
|
||||||
|
render(
|
||||||
|
<PreviewLogsToggleButton
|
||||||
|
logsState={logsState}
|
||||||
|
onToggle={onToggleLogs}
|
||||||
|
showLogs={showLogs}
|
||||||
|
showText={showText}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
describe('basic toggle functionality', function() {
|
describe('basic toggle functionality', function() {
|
||||||
const logsState = {
|
const logsState = {
|
||||||
nErrors: 0,
|
nErrors: 0,
|
||||||
|
@ -13,24 +31,12 @@ describe('<PreviewLogsToggleButton />', function() {
|
||||||
const onToggleLogs = () => {}
|
const onToggleLogs = () => {}
|
||||||
it('should render a view logs button when previewing the PDF', function() {
|
it('should render a view logs button when previewing the PDF', function() {
|
||||||
const showLogs = false
|
const showLogs = false
|
||||||
render(
|
renderPreviewLogsToggleButton(logsState, onToggleLogs, showLogs)
|
||||||
<PreviewLogsToggleButton
|
|
||||||
logsState={logsState}
|
|
||||||
showLogs={showLogs}
|
|
||||||
onToggle={onToggleLogs}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
screen.getByText('View logs')
|
screen.getByText('View logs')
|
||||||
})
|
})
|
||||||
it('should render a view PDF button when viewing logs', function() {
|
it('should render a view PDF button when viewing logs', function() {
|
||||||
const showLogs = true
|
const showLogs = true
|
||||||
render(
|
renderPreviewLogsToggleButton(logsState, onToggleLogs, showLogs)
|
||||||
<PreviewLogsToggleButton
|
|
||||||
logsState={logsState}
|
|
||||||
showLogs={showLogs}
|
|
||||||
onToggle={onToggleLogs}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
screen.getByText('View PDF')
|
screen.getByText('View PDF')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -43,13 +49,7 @@ describe('<PreviewLogsToggleButton />', function() {
|
||||||
nWarnings: 0,
|
nWarnings: 0,
|
||||||
nLogEntries: 0
|
nLogEntries: 0
|
||||||
}
|
}
|
||||||
render(
|
renderPreviewLogsToggleButton(logsState, onToggleLogs, showLogs)
|
||||||
<PreviewLogsToggleButton
|
|
||||||
logsState={logsState}
|
|
||||||
showLogs={showLogs}
|
|
||||||
onToggle={onToggleLogs}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
screen.getByText('View logs')
|
screen.getByText('View logs')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -59,13 +59,7 @@ describe('<PreviewLogsToggleButton />', function() {
|
||||||
nWarnings: 0,
|
nWarnings: 0,
|
||||||
nLogEntries: 0
|
nLogEntries: 0
|
||||||
}
|
}
|
||||||
render(
|
renderPreviewLogsToggleButton(logsState, onToggleLogs, showLogs)
|
||||||
<PreviewLogsToggleButton
|
|
||||||
logsState={logsState}
|
|
||||||
showLogs={showLogs}
|
|
||||||
onToggle={onToggleLogs}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
screen.getByText(`Your project has errors (${logsState.nErrors})`)
|
screen.getByText(`Your project has errors (${logsState.nErrors})`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -75,13 +69,7 @@ describe('<PreviewLogsToggleButton />', function() {
|
||||||
nWarnings: 1,
|
nWarnings: 1,
|
||||||
nLogEntries: 0
|
nLogEntries: 0
|
||||||
}
|
}
|
||||||
render(
|
renderPreviewLogsToggleButton(logsState, onToggleLogs, showLogs)
|
||||||
<PreviewLogsToggleButton
|
|
||||||
logsState={logsState}
|
|
||||||
showLogs={showLogs}
|
|
||||||
onToggle={onToggleLogs}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
screen.getByText(`Your project has errors (${logsState.nErrors})`)
|
screen.getByText(`Your project has errors (${logsState.nErrors})`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -91,13 +79,7 @@ describe('<PreviewLogsToggleButton />', function() {
|
||||||
nWarnings: 1,
|
nWarnings: 1,
|
||||||
nLogEntries: 0
|
nLogEntries: 0
|
||||||
}
|
}
|
||||||
render(
|
renderPreviewLogsToggleButton(logsState, onToggleLogs, showLogs)
|
||||||
<PreviewLogsToggleButton
|
|
||||||
logsState={logsState}
|
|
||||||
showLogs={showLogs}
|
|
||||||
onToggle={onToggleLogs}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
screen.getByText(`View warnings (${logsState.nWarnings})`)
|
screen.getByText(`View warnings (${logsState.nWarnings})`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -107,14 +89,30 @@ describe('<PreviewLogsToggleButton />', function() {
|
||||||
nWarnings: 0,
|
nWarnings: 0,
|
||||||
nLogEntries: 0
|
nLogEntries: 0
|
||||||
}
|
}
|
||||||
render(
|
renderPreviewLogsToggleButton(logsState, onToggleLogs, showLogs)
|
||||||
<PreviewLogsToggleButton
|
|
||||||
logsState={logsState}
|
|
||||||
showLogs={showLogs}
|
|
||||||
onToggle={onToggleLogs}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
screen.getByText('Your project has errors (9+)')
|
screen.getByText('Your project has errors (9+)')
|
||||||
})
|
})
|
||||||
|
it('should show the button text when prop showText=true', function() {
|
||||||
|
const logsState = {
|
||||||
|
nErrors: 0,
|
||||||
|
nWarnings: 0,
|
||||||
|
nLogEntries: 0
|
||||||
|
}
|
||||||
|
const showText = true
|
||||||
|
renderPreviewLogsToggleButton(logsState, onToggleLogs, showLogs, showText)
|
||||||
|
expect(screen.getByText('View logs').getAttribute('style')).to.be.null
|
||||||
|
})
|
||||||
|
it('should not show the button text when prop showText=false', function() {
|
||||||
|
const logsState = {
|
||||||
|
nErrors: 0,
|
||||||
|
nWarnings: 0,
|
||||||
|
nLogEntries: 0
|
||||||
|
}
|
||||||
|
const showText = false
|
||||||
|
renderPreviewLogsToggleButton(logsState, onToggleLogs, showLogs, showText)
|
||||||
|
expect(screen.getByText('View logs').getAttribute('style')).to.equal(
|
||||||
|
'position: absolute; right: -100vw;'
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -95,10 +95,24 @@ describe('<PreviewRecompileButton />', function() {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
function renderPreviewRecompileButton(compilerState = {}) {
|
it('should show the button text when prop showText=true', function() {
|
||||||
|
const showText = true
|
||||||
|
renderPreviewRecompileButton({}, showText)
|
||||||
|
expect(screen.getByText('Recompile').getAttribute('style')).to.be.null
|
||||||
|
})
|
||||||
|
it('should not show the button text when prop showText=false', function() {
|
||||||
|
const showText = false
|
||||||
|
renderPreviewRecompileButton({}, showText)
|
||||||
|
expect(screen.getByText('Recompile').getAttribute('style')).to.equal(
|
||||||
|
'position: absolute; right: -100vw;'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
function renderPreviewRecompileButton(compilerState = {}, showText) {
|
||||||
if (!compilerState.logEntries) {
|
if (!compilerState.logEntries) {
|
||||||
compilerState.logEntries = {}
|
compilerState.logEntries = {}
|
||||||
}
|
}
|
||||||
|
if (showText === undefined) showText = true
|
||||||
render(
|
render(
|
||||||
<PreviewRecompileButton
|
<PreviewRecompileButton
|
||||||
compilerState={{
|
compilerState={{
|
||||||
|
@ -115,6 +129,7 @@ describe('<PreviewRecompileButton />', function() {
|
||||||
onSetDraftMode={() => {}}
|
onSetDraftMode={() => {}}
|
||||||
onSetSyntaxCheck={() => {}}
|
onSetSyntaxCheck={() => {}}
|
||||||
onClearCache={onClearCache}
|
onClearCache={onClearCache}
|
||||||
|
showText={showText}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
import React from 'react'
|
||||||
|
import sinon from 'sinon'
|
||||||
|
import { expect } from 'chai'
|
||||||
|
import { screen, render } from '@testing-library/react'
|
||||||
|
import PreviewToolbar from '../../../../../frontend/js/features/preview/components/preview-toolbar'
|
||||||
|
|
||||||
|
describe('<PreviewToolbar />', function() {
|
||||||
|
const onClearCache = sinon.stub()
|
||||||
|
const onRecompile = sinon.stub()
|
||||||
|
const onRunSyntaxCheckNow = sinon.stub()
|
||||||
|
const onSetAutoCompile = sinon.stub()
|
||||||
|
const onSetDraftMode = sinon.stub()
|
||||||
|
const onSetSyntaxCheck = sinon.stub()
|
||||||
|
const onToggleLogs = sinon.stub()
|
||||||
|
|
||||||
|
function renderPreviewToolbar(compilerState = {}, logState = {}, showLogs) {
|
||||||
|
render(
|
||||||
|
<PreviewToolbar
|
||||||
|
compilerState={{
|
||||||
|
isAutoCompileOn: true,
|
||||||
|
isClearingCache: false,
|
||||||
|
isCompiling: false,
|
||||||
|
isDraftModeOn: false,
|
||||||
|
isSyntaxCheckOn: false,
|
||||||
|
logEntries: {},
|
||||||
|
...compilerState
|
||||||
|
}}
|
||||||
|
logsState={{ nErrors: 0, nWarnings: 0, nLogEntries: 0, ...logState }}
|
||||||
|
onClearCache={onClearCache}
|
||||||
|
onRecompile={onRecompile}
|
||||||
|
onRunSyntaxCheckNow={onRunSyntaxCheckNow}
|
||||||
|
onSetAutoCompile={onSetAutoCompile}
|
||||||
|
onSetDraftMode={onSetDraftMode}
|
||||||
|
onSetSyntaxCheck={onSetSyntaxCheck}
|
||||||
|
onToggleLogs={onToggleLogs}
|
||||||
|
outputFiles={[]}
|
||||||
|
pdfDownloadUrl="/download-pdf-url"
|
||||||
|
showLogs={showLogs || false}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
it('renders the toolbar', function() {
|
||||||
|
renderPreviewToolbar()
|
||||||
|
screen.getByText('Recompile')
|
||||||
|
screen.getByText('Download PDF')
|
||||||
|
screen.getByText('View logs')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('all toolbar items have "toolbar-item" class and text has "toolbar-text"', function() {
|
||||||
|
renderPreviewToolbar()
|
||||||
|
const toolbar = screen.getByTestId('toolbar-preview')
|
||||||
|
for (const toolbarSection of toolbar.children) {
|
||||||
|
for (const toolbarItem of toolbarSection.children) {
|
||||||
|
expect(toolbarItem.className).to.contain('toolbar-item')
|
||||||
|
for (const parts of toolbarItem.children) {
|
||||||
|
for (const part of parts.children) {
|
||||||
|
if (part.nodeName !== 'LI' && part.textContent) {
|
||||||
|
expect(part.className).to.contain('toolbar-text')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
Loading…
Reference in a new issue