+
diff --git a/services/web/frontend/js/shared/hooks/use-resize-observer.js b/services/web/frontend/js/shared/hooks/use-resize-observer.js
new file mode 100644
index 0000000000..c88f0a6554
--- /dev/null
+++ b/services/web/frontend/js/shared/hooks/use-resize-observer.js
@@ -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
diff --git a/services/web/frontend/stylesheets/app/editor/pdf.less b/services/web/frontend/stylesheets/app/editor/pdf.less
index 3eaeacea07..65932fb1db 100644
--- a/services/web/frontend/stylesheets/app/editor/pdf.less
+++ b/services/web/frontend/stylesheets/app/editor/pdf.less
@@ -103,6 +103,10 @@
margin-left: @line-height-computed / 4;
}
+.toolbar-text {
+ padding-left: @padding-xs;
+}
+
.pdf-viewer {
iframe {
width: 100%;
diff --git a/services/web/test/frontend/features/preview/components/preview-download-button.test.js b/services/web/test/frontend/features/preview/components/preview-download-button.test.js
index 91c80b131b..029cc958ab 100644
--- a/services/web/test/frontend/features/preview/components/preview-download-button.test.js
+++ b/services/web/test/frontend/features/preview/components/preview-download-button.test.js
@@ -19,17 +19,36 @@ describe('
', function() {
}
}
- it('should disable the button and dropdown toggle when compiling', function() {
- const isCompiling = true
- const outputFiles = undefined
+ function renderPreviewDownloadButton(
+ isCompiling,
+ outputFiles,
+ pdfDownloadUrl,
+ showText
+ ) {
+ if (isCompiling === undefined) isCompiling = false
+ if (showText === undefined) showText = true
render(
)
- 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')
expect(buttons.length).to.equal(1) // the dropdown toggle
expect(buttons[0].getAttribute('disabled')).to.exist
@@ -40,41 +59,35 @@ describe('
', function() {
it('should disable the PDF button when there is no PDF', function() {
const isCompiling = false
const outputFiles = []
- render(
-
- )
- expect(screen.getByText('Download PDF').getAttribute('disabled')).to.exist
+ renderPreviewDownloadButton(isCompiling, outputFiles)
+ expect(
+ screen
+ .getByText('Download PDF')
+ .closest('a')
+ .getAttribute('disabled')
+ ).to.exist
})
it('should enable the PDF button when there is a main PDF', function() {
const isCompiling = false
const outputFiles = []
- render(
-
- )
- expect(screen.getByText('Download PDF').getAttribute('href')).to.equal(
- pdfDownloadUrl
- )
- expect(screen.getByText('Download PDF').getAttribute('disabled')).to.not
- .exist
+ renderPreviewDownloadButton(isCompiling, outputFiles, pdfDownloadUrl)
+ expect(
+ screen
+ .getByText('Download PDF')
+ .closest('a')
+ .getAttribute('href')
+ ).to.equal(pdfDownloadUrl)
+ expect(
+ screen
+ .getByText('Download PDF')
+ .closest('a')
+ .getAttribute('disabled')
+ ).to.not.exist
})
it('should enable the dropdown when not compiling', function() {
const isCompiling = false
const outputFiles = []
- render(
-
- )
+ renderPreviewDownloadButton(isCompiling, outputFiles, pdfDownloadUrl)
const buttons = screen.getAllByRole('button')
expect(buttons[0]).to.exist
expect(buttons[0].getAttribute('disabled')).to.not.exist
@@ -93,13 +106,7 @@ describe('
', function() {
makeFile('output.blg')
]
- render(
-
- )
+ renderPreviewDownloadButton(isCompiling, outputFiles, pdfDownloadUrl)
const menuItems = screen.getAllByRole('menuitem')
expect(menuItems.length).to.equal(outputFiles.length - 1) // main PDF is listed separately
@@ -138,13 +145,9 @@ describe('
', function() {
const pdfFile = makeFile('output.pdf', true)
const bblFile = makeFile('output.bbl')
const outputFiles = [Object.assign({}, { ...bblFile }), bblFile, pdfFile]
- render(
-
- )
+
+ renderPreviewDownloadButton(isCompiling, outputFiles, pdfDownloadUrl)
+
const bblMenuItems = screen.getAllByText((content, element) => {
return (
content !== '' && element.textContent === 'Download output.bbl file'
@@ -157,16 +160,23 @@ describe('
', function() {
const pdfFile = makeFile('output.pdf', true)
const pdfAltFile = makeFile('alt.pdf')
const outputFiles = [pdfFile, pdfAltFile]
-
- render(
-
- )
+ renderPreviewDownloadButton(isCompiling, outputFiles, pdfDownloadUrl)
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() {
it('should display when there are top files and other files', function() {
const outputFiles = [
@@ -176,13 +186,7 @@ describe('
', function() {
makeFile('output.log')
]
- render(
-
- )
+ renderPreviewDownloadButton(false, outputFiles, pdfDownloadUrl, true)
screen.getByText('Other output files')
screen.getByRole('separator')
@@ -194,13 +198,7 @@ describe('
', function() {
makeFile('output.gls')
]
- render(
-
- )
+ renderPreviewDownloadButton(false, outputFiles, pdfDownloadUrl, true)
expect(screen.queryByText('Other output files')).to.not.exist
expect(screen.queryByRole('separator')).to.not.exist
@@ -208,13 +206,7 @@ describe('
', function() {
it('should not display when there are other files and no top files', function() {
const outputFiles = [makeFile('output.log')]
- render(
-
- )
+ renderPreviewDownloadButton(false, outputFiles, pdfDownloadUrl, true)
expect(screen.queryByText('Other output files')).to.not.exist
expect(screen.queryByRole('separator')).to.not.exist
diff --git a/services/web/test/frontend/features/preview/components/preview-logs-toggle-button.test.js b/services/web/test/frontend/features/preview/components/preview-logs-toggle-button.test.js
index 7be17d8431..e020a44a61 100644
--- a/services/web/test/frontend/features/preview/components/preview-logs-toggle-button.test.js
+++ b/services/web/test/frontend/features/preview/components/preview-logs-toggle-button.test.js
@@ -1,9 +1,27 @@
import React from 'react'
+import { expect } from 'chai'
import { screen, render } from '@testing-library/react'
import PreviewLogsToggleButton from '../../../../../frontend/js/features/preview/components/preview-logs-toggle-button'
describe('
', function() {
+ function renderPreviewLogsToggleButton(
+ logsState,
+ onToggleLogs,
+ showLogs,
+ showText
+ ) {
+ if (showText === undefined) showText = true
+ render(
+
+ )
+ }
+
describe('basic toggle functionality', function() {
const logsState = {
nErrors: 0,
@@ -13,24 +31,12 @@ describe('
', function() {
const onToggleLogs = () => {}
it('should render a view logs button when previewing the PDF', function() {
const showLogs = false
- render(
-
- )
+ renderPreviewLogsToggleButton(logsState, onToggleLogs, showLogs)
screen.getByText('View logs')
})
it('should render a view PDF button when viewing logs', function() {
const showLogs = true
- render(
-
- )
+ renderPreviewLogsToggleButton(logsState, onToggleLogs, showLogs)
screen.getByText('View PDF')
})
})
@@ -43,13 +49,7 @@ describe('
', function() {
nWarnings: 0,
nLogEntries: 0
}
- render(
-
- )
+ renderPreviewLogsToggleButton(logsState, onToggleLogs, showLogs)
screen.getByText('View logs')
})
@@ -59,13 +59,7 @@ describe('
', function() {
nWarnings: 0,
nLogEntries: 0
}
- render(
-
- )
+ renderPreviewLogsToggleButton(logsState, onToggleLogs, showLogs)
screen.getByText(`Your project has errors (${logsState.nErrors})`)
})
@@ -75,13 +69,7 @@ describe('
', function() {
nWarnings: 1,
nLogEntries: 0
}
- render(
-
- )
+ renderPreviewLogsToggleButton(logsState, onToggleLogs, showLogs)
screen.getByText(`Your project has errors (${logsState.nErrors})`)
})
@@ -91,13 +79,7 @@ describe('
', function() {
nWarnings: 1,
nLogEntries: 0
}
- render(
-
- )
+ renderPreviewLogsToggleButton(logsState, onToggleLogs, showLogs)
screen.getByText(`View warnings (${logsState.nWarnings})`)
})
@@ -107,14 +89,30 @@ describe('
', function() {
nWarnings: 0,
nLogEntries: 0
}
- render(
-
- )
+ renderPreviewLogsToggleButton(logsState, onToggleLogs, showLogs)
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;'
+ )
+ })
})
})
diff --git a/services/web/test/frontend/features/preview/components/preview-recompile-button.test.js b/services/web/test/frontend/features/preview/components/preview-recompile-button.test.js
index 1243fe0127..e31e810c15 100644
--- a/services/web/test/frontend/features/preview/components/preview-recompile-button.test.js
+++ b/services/web/test/frontend/features/preview/components/preview-recompile-button.test.js
@@ -95,10 +95,24 @@ describe('
', 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) {
compilerState.logEntries = {}
}
+ if (showText === undefined) showText = true
render(
', function() {
onSetDraftMode={() => {}}
onSetSyntaxCheck={() => {}}
onClearCache={onClearCache}
+ showText={showText}
/>
)
}
diff --git a/services/web/test/frontend/features/preview/components/preview-toolbar.test.js b/services/web/test/frontend/features/preview/components/preview-toolbar.test.js
new file mode 100644
index 0000000000..46fa9fbdf2
--- /dev/null
+++ b/services/web/test/frontend/features/preview/components/preview-toolbar.test.js
@@ -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('
', 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(
+
+ )
+ }
+
+ 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')
+ }
+ }
+ }
+ }
+ }
+ })
+})