From 21ffe27bddbfef1595de018f40d7022b58e555f9 Mon Sep 17 00:00:00 2001 From: Jessica Lawshe Date: Mon, 9 Nov 2020 08:52:22 -0600 Subject: [PATCH] Merge pull request #3330 from overleaf/jel-toolbar-btns Hide toolbar text and show tooltip when out of space GitOrigin-RevId: 5a73b69e7d92695c4f8691a747307908550e3790 --- .../components/preview-download-button.js | 60 ++++-- .../components/preview-logs-toggle-button.js | 94 +++++++-- .../components/preview-recompile-button.js | 68 +++++-- .../preview/components/preview-toolbar.js | 178 +++++++++++++++++- .../js/shared/hooks/use-resize-observer.js | 38 ++++ .../frontend/stylesheets/app/editor/pdf.less | 4 + .../preview-download-button.test.js | 144 +++++++------- .../preview-logs-toggle-button.test.js | 96 +++++----- .../preview-recompile-button.test.js | 17 +- .../components/preview-toolbar.test.js | 66 +++++++ 10 files changed, 593 insertions(+), 172 deletions(-) create mode 100644 services/web/frontend/js/shared/hooks/use-resize-observer.js create mode 100644 services/web/test/frontend/features/preview/components/preview-toolbar.test.js diff --git a/services/web/frontend/js/features/preview/components/preview-download-button.js b/services/web/frontend/js/features/preview/components/preview-download-button.js index 464bcbfa3d..00eb928f65 100644 --- a/services/web/frontend/js/features/preview/components/preview-download-button.js +++ b/services/web/frontend/js/features/preview/components/preview-download-button.js @@ -1,12 +1,17 @@ import React from 'react' 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 Icon from '../../../shared/components/icon' export const topFileTypes = ['bbl', 'gls', 'ind'] -function PreviewDownloadButton({ isCompiling, outputFiles, pdfDownloadUrl }) { +function PreviewDownloadButton({ + isCompiling, + outputFiles, + pdfDownloadUrl, + showText +}) { let topFiles = [] let otherFiles = [] const { t } = useTranslation() @@ -26,16 +31,46 @@ function PreviewDownloadButton({ isCompiling, outputFiles, pdfDownloadUrl }) { }) } + let textStyle = {} + if (!showText) { + textStyle = { + position: 'absolute', + right: '-100vw' + } + } + + const buttonElement = ( + + + + {t('download_pdf')} + + + ) + return ( - - - {t('download_pdf')} - + + {showText ? ( + buttonElement + ) : ( + {t('download_pdf')} + } + > + {buttonElement} + + )} {showLogs ? ( - + ) : ( - + )} ) + + return showText ? ( + buttonElement + ) : ( + + {showLogs ? t('view_pdf') : t('view_logs')} + + } + > + {buttonElement} + + ) } -function CompilationResultIndicator({ nErrors, nWarnings }) { +function CompilationResultIndicator({ textStyle, nErrors, nWarnings }) { if (nErrors || nWarnings) { return ( ) } else { - return + return } } -function LogsCompilationResultIndicator({ logType, nLogs }) { +function LogsCompilationResultIndicator({ textStyle, logType, nLogs }) { const { t } = useTranslation() const label = logType === 'errors' ? t('your_project_has_errors') : t('view_warnings') return ( <> - + {`${label} (${nLogs > 9 ? '9+' : nLogs})`} ) } -function ViewLogsButton() { +function ViewLogsButton({ textStyle }) { const { t } = useTranslation() return ( <> - {t('view_logs')} + + {t('view_logs')} + ) } -function ViewPdfButton() { +function ViewPdfButton({ textStyle }) { const { t } = useTranslation() return ( <> - {t('view_pdf')} + + {t('view_pdf')} + ) } @@ -89,12 +135,22 @@ PreviewLogsToggleButton.propTypes = { nWarnings: PropTypes.number.isRequired, nLogEntries: PropTypes.number.isRequired }), - showLogs: PropTypes.bool.isRequired + showLogs: PropTypes.bool.isRequired, + showText: PropTypes.bool.isRequired } LogsCompilationResultIndicator.propTypes = { 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 diff --git a/services/web/frontend/js/features/preview/components/preview-recompile-button.js b/services/web/frontend/js/features/preview/components/preview-recompile-button.js index f93530d53d..dcaf6e971e 100644 --- a/services/web/frontend/js/features/preview/components/preview-recompile-button.js +++ b/services/web/frontend/js/features/preview/components/preview-recompile-button.js @@ -1,6 +1,6 @@ import React from 'react' 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 Icon from '../../../shared/components/icon' @@ -17,7 +17,8 @@ function PreviewRecompileButton({ onRunSyntaxCheckNow, onSetAutoCompile, onSetDraftMode, - onSetSyntaxCheck + onSetSyntaxCheck, + showText }) { const { t } = useTranslation() @@ -55,18 +56,43 @@ function PreviewRecompileButton({ onSetSyntaxCheck(false) } - return ( - + let compilingProps = {} + 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 = ( + ) + + return showText ? ( + buttonElement + ) : ( + + {isCompiling || isClearingCache ? t('compiling') : t('recompile')} + + } + > + {buttonElement} + + ) } PreviewRecompileButton.propTypes = { @@ -131,7 +172,8 @@ PreviewRecompileButton.propTypes = { onRunSyntaxCheckNow: PropTypes.func.isRequired, onSetAutoCompile: PropTypes.func.isRequired, onSetDraftMode: PropTypes.func.isRequired, - onSetSyntaxCheck: PropTypes.func.isRequired + onSetSyntaxCheck: PropTypes.func.isRequired, + showText: PropTypes.bool.isRequired } export default PreviewRecompileButton diff --git a/services/web/frontend/js/features/preview/components/preview-toolbar.js b/services/web/frontend/js/features/preview/components/preview-toolbar.js index 67b9af5fc9..af5c63907f 100644 --- a/services/web/frontend/js/features/preview/components/preview-toolbar.js +++ b/services/web/frontend/js/features/preview/components/preview-toolbar.js @@ -1,8 +1,14 @@ -import React from 'react' +import React, { useRef, useState } from 'react' import PropTypes from 'prop-types' import PreviewDownloadButton from './preview-download-button' import PreviewRecompileButton from './preview-recompile-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({ compilerState, @@ -18,8 +24,173 @@ function PreviewToolbar({ pdfDownloadUrl, 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 ( -
+
@@ -41,6 +214,7 @@ function PreviewToolbar({ logsState={logsState} showLogs={showLogs} onToggle={onToggleLogs} + showText={showToggleText} />
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') + } + } + } + } + } + }) +})