diff --git a/src/lib/Preview.js b/src/lib/Preview.js index 1fca1f1a9..76f5b1b2e 100644 --- a/src/lib/Preview.js +++ b/src/lib/Preview.js @@ -925,6 +925,12 @@ class Preview extends EventEmitter { // Whether annotations v2 should be shown this.options.showAnnotations = !!options.showAnnotations; + // Whether the loading indicator should be shown + this.options.showLoading = options.showLoading !== false; + + // Whether the progress indicator should be shown + this.options.showProgress = options.showProgress !== false; + // Whether annotations v4 buttons should be shown in toolbar this.options.showAnnotationsControls = !!options.showAnnotationsControls; diff --git a/src/lib/PreviewUI.js b/src/lib/PreviewUI.js index ccd6bc9a8..6e2e6448f 100644 --- a/src/lib/PreviewUI.js +++ b/src/lib/PreviewUI.js @@ -1,7 +1,6 @@ import ProgressBar from './ProgressBar'; import shellTemplate from './shell.html'; import Notification from './Notification'; -import { insertTemplate } from './util'; import { CLASS_BOX_PREVIEW_BASE_HEADER, CLASS_BOX_PREVIEW_HAS_HEADER, @@ -25,7 +24,10 @@ import { SELECTOR_NAVIGATION_LEFT, SELECTOR_NAVIGATION_RIGHT, SELECTOR_BOX_PREVIEW_CONTENT, + SELECTOR_BOX_PREVIEW_ICON, } from './constants'; +import { getIconFromName, getIconFromExtension } from './icons/icons'; +import { insertTemplate } from './util'; class PreviewUI { /** @property {HTMLElement} - Container element */ @@ -122,10 +124,16 @@ class PreviewUI { } // Setup progress bar - this.progressBar = new ProgressBar(this.container); + if (options.showProgress) { + this.progressBar = new ProgressBar(this.container); + } // Setup loading indicator - this.setupLoading(); + if (options.showLoading) { + this.setupLoading(); + } else { + this.destroyLoading(); + } // Attach keyboard events document.addEventListener('keydown', this.keydownHandler); @@ -258,7 +266,7 @@ class PreviewUI { } /** - * Shows the loading indicator + * Shows the loading indicator. * * @public * @return {void} @@ -281,14 +289,49 @@ class PreviewUI { } this.previewContainer.classList.add(CLASS_PREVIEW_LOADED); + this.showCrawler(); + } + + /** + * Hides the loading crawler. + * + * @public + * @return {void} + */ + hideCrawler() { + const crawler = this.previewContainer.querySelector(SELECTOR_BOX_PREVIEW_CRAWLER_WRAPPER); + if (crawler) { + crawler.classList.add(CLASS_HIDDEN); + } + } - // Re-show the cralwer for the next preview since it is hidden in finishLoadingSetup() in BaseViewer.js + /** + * Shows the loading crawler. + * + * @public + * @return {void} + */ + showCrawler() { const crawler = this.previewContainer.querySelector(SELECTOR_BOX_PREVIEW_CRAWLER_WRAPPER); if (crawler) { crawler.classList.remove(CLASS_HIDDEN); } } + /** + * Set the icon for the loading indicator based on the file extension. + * + * @public + * @param {string} extension - File extension + * @return {void} + */ + setLoadingIcon(extension) { + const iconWrapperEl = this.container.querySelector(SELECTOR_BOX_PREVIEW_ICON); + if (iconWrapperEl) { + iconWrapperEl.innerHTML = getIconFromExtension(extension) || getIconFromName('FILE_DEFAULT'); + } + } + /** * Shows and starts a progress bar at the top of the preview. * @@ -296,7 +339,9 @@ class PreviewUI { * @return {void} */ startProgressBar() { - this.progressBar.start(); + if (this.progressBar) { + this.progressBar.start(); + } } /** @@ -306,7 +351,9 @@ class PreviewUI { * @return {void} */ finishProgressBar() { - this.progressBar.finish(); + if (this.progressBar) { + this.progressBar.finish(); + } } /** @@ -376,6 +423,21 @@ class PreviewUI { //-------------------------------------------------------------------------- // Private //-------------------------------------------------------------------------- + /** + * Remove global loading indicators from the shell + * + * @private + * @return {void} + */ + destroyLoading() { + const loadingWrapperEl = this.container.querySelector(SELECTOR_BOX_PREVIEW_LOADING_WRAPPER); + if (!loadingWrapperEl) { + return; + } + + loadingWrapperEl.parentElement.removeChild(loadingWrapperEl); + } + /** * Sets up the preview header. * diff --git a/src/lib/__tests__/Preview-test.js b/src/lib/__tests__/Preview-test.js index 61647f364..7aa89a313 100644 --- a/src/lib/__tests__/Preview-test.js +++ b/src/lib/__tests__/Preview-test.js @@ -1310,6 +1310,24 @@ describe('lib/Preview', () => { expect(preview.options.showAnnotations).toBe(true); }); + test('should set whether to show loading indicators', () => { + preview.parseOptions(preview.previewOptions); + expect(preview.options.showLoading).toBe(true); + + preview.previewOptions.showLoading = false; + preview.parseOptions(preview.previewOptions); + expect(preview.options.showLoading).toBe(false); + }); + + test('should set whether to show progress indicators', () => { + preview.parseOptions(preview.previewOptions); + expect(preview.options.showProgress).toBe(true); + + preview.previewOptions.showProgress = false; + preview.parseOptions(preview.previewOptions); + expect(preview.options.showProgress).toBe(false); + }); + test('should set whether to skip load from the server and any server updates', () => { preview.parseOptions(preview.previewOptions); expect(preview.options.skipServerUpdate).toBe(false); diff --git a/src/lib/__tests__/PreviewUI-test.js b/src/lib/__tests__/PreviewUI-test.js index eeb139321..a5d49ac9d 100644 --- a/src/lib/__tests__/PreviewUI-test.js +++ b/src/lib/__tests__/PreviewUI-test.js @@ -1,6 +1,7 @@ /* eslint-disable no-unused-expressions */ import * as constants from '../constants'; import PreviewUI from '../PreviewUI'; +import { getIconFromExtension } from '../icons/icons'; const sandbox = sinon.createSandbox(); let ui; @@ -19,6 +20,8 @@ describe('lib/PreviewUI', () => { containerEl = document.querySelector('.ui'); options = { container: containerEl, + showLoading: true, + showProgress: true, }; }); @@ -64,6 +67,17 @@ describe('lib/PreviewUI', () => { expect(loadingWrapperEl).toContainHTML('Download File'); }); + test('should not setup the progress bar or loading state if their respective option is false', () => { + const resultEl = ui.setup({ container: containerEl, showLoading: false, showProgress: false }); + expect(resultEl).not.toContainSelector(constants.SELECTOR_BOX_PREVIEW_PROGRESS_BAR); + + // Check loading state + expect(resultEl).not.toContainSelector(constants.SELECTOR_BOX_PREVIEW_LOADING_WRAPPER); + expect(resultEl).not.toContainSelector(constants.SELECTOR_BOX_PREVIEW_ICON); + expect(resultEl).not.toContainHTML('Loading Preview...'); + expect(resultEl).not.toContainHTML('Download File'); + }); + test('should setup logo if option specifies', () => { const url = 'http://someurl.com/'; options.logoUrl = url; @@ -231,19 +245,38 @@ describe('lib/PreviewUI', () => { }); describe('hideLoadingIndicator()', () => { + beforeEach(() => { + jest.spyOn(ui, 'showCrawler'); + }); + test('should hide loading indicator', () => { const contentContainerEl = containerEl.querySelector(constants.SELECTOR_BOX_PREVIEW); ui.hideLoadingIndicator(); expect(contentContainerEl).toHaveClass(constants.CLASS_PREVIEW_LOADED); }); + test('should show the crawler', () => { + ui.hideLoadingIndicator(); + expect(ui.showCrawler).toBeCalled(); + }); + }); + + describe('showCrawler()', () => { test('should remove the hidden class from the crawler', () => { const crawlerEl = containerEl.querySelector(constants.SELECTOR_BOX_PREVIEW_CRAWLER_WRAPPER); - ui.hideLoadingIndicator(); + ui.showCrawler(); expect(crawlerEl).not.toHaveClass(constants.CLASS_HIDDEN); }); }); + describe('hideCrawler()', () => { + test('should add the hidden class to the crawler', () => { + const crawlerEl = containerEl.querySelector(constants.SELECTOR_BOX_PREVIEW_CRAWLER_WRAPPER); + ui.hideCrawler(); + expect(crawlerEl).toHaveClass(constants.CLASS_HIDDEN); + }); + }); + describe('setupNotification()', () => { test('should set up the notification', () => { ui.setupNotification(); @@ -252,6 +285,18 @@ describe('lib/PreviewUI', () => { }); }); + describe('setLoadingIcon()', () => { + test('should hide the crawler and set the file icon into the icon element', () => { + const iconEl = document.createElement('div'); + iconEl.innerHTML = getIconFromExtension('pdf'); + + ui.setup(options); + ui.setLoadingIcon('pdf'); + + expect(containerEl.querySelector(constants.SELECTOR_BOX_PREVIEW_ICON).innerHTML).toEqual(iconEl.innerHTML); + }); + }); + describe('startProgressBar()', () => { test('should start the progress bar', () => { ui.progressBar = { diff --git a/src/lib/viewers/BaseViewer.js b/src/lib/viewers/BaseViewer.js index f6bef1253..fbfda6cde 100644 --- a/src/lib/viewers/BaseViewer.js +++ b/src/lib/viewers/BaseViewer.js @@ -24,19 +24,15 @@ import { CLASS_ANNOTATIONS_CREATE_REGION, CLASS_ANNOTATIONS_DISCOVERABLE, CLASS_BOX_PREVIEW_MOBILE, - CLASS_HIDDEN, FILE_OPTION_START, SELECTOR_BOX_PREVIEW_BTN_ANNOTATE_DRAW, SELECTOR_BOX_PREVIEW_BTN_ANNOTATE_POINT, SELECTOR_BOX_PREVIEW_CONTENT, - SELECTOR_BOX_PREVIEW_CRAWLER_WRAPPER, - SELECTOR_BOX_PREVIEW_ICON, SELECTOR_BOX_PREVIEW, STATUS_SUCCESS, STATUS_VIEWABLE, } from '../constants'; import { EXCLUDED_EXTENSIONS } from '../extensions'; -import { getIconFromExtension, getIconFromName } from '../icons/icons'; import { VIEWER_EVENT, ERROR_CODE, LOAD_METRIC, DOWNLOAD_REACHABILITY_METRICS } from '../events'; import AnnotationControlsFSM, { AnnotationInput, AnnotationMode } from '../AnnotationControlsFSM'; import AnnotationModule from '../AnnotationModule'; @@ -73,8 +69,6 @@ const ANNOTATION_BUTTONS = { }, }; -const DEFAULT_FILE_ICON_NAME = 'FILE_DEFAULT'; - class BaseViewer extends EventEmitter { /** @property {Api} - Api instance used for XHR calls */ api; @@ -186,13 +180,9 @@ class BaseViewer extends EventEmitter { } if (this.options.file) { - const fileExt = this.options.file.extension; - this.fileLoadingIcon = getIconFromExtension(fileExt); this.startAt = getProp(this.options, `fileOptions.${this.options.file.id}.${FILE_OPTION_START}`, {}); } - this.finishLoadingSetup(); - // Get the container dom element if selector was passed, in tests let { container } = this.options; if (typeof container === 'string') { @@ -204,6 +194,9 @@ class BaseViewer extends EventEmitter { // From the perspective of viewers bp-content holds everything this.containerEl = container.querySelector(SELECTOR_BOX_PREVIEW_CONTENT); + // Update the loading indicators + this.setupLoading(); + // Attach event listeners this.addCommonListeners(); @@ -235,16 +228,12 @@ class BaseViewer extends EventEmitter { * * @return {void} */ - finishLoadingSetup() { - const { container } = this.options; - const crawler = container.querySelector(SELECTOR_BOX_PREVIEW_CRAWLER_WRAPPER); - if (crawler) { - crawler.classList.add(CLASS_HIDDEN); - } + setupLoading() { + const { file = {} } = this.options; - const iconWrapperEl = container.querySelector(SELECTOR_BOX_PREVIEW_ICON); - if (iconWrapperEl) { - iconWrapperEl.innerHTML = this.fileLoadingIcon || getIconFromName(DEFAULT_FILE_ICON_NAME); + if (this.previewUI) { + this.previewUI.hideCrawler(); + this.previewUI.setLoadingIcon(file.extension); } } diff --git a/src/lib/viewers/__tests__/BaseViewer-test.js b/src/lib/viewers/__tests__/BaseViewer-test.js index 37e9cebb3..27b289ac3 100644 --- a/src/lib/viewers/__tests__/BaseViewer-test.js +++ b/src/lib/viewers/__tests__/BaseViewer-test.js @@ -1,7 +1,6 @@ /* eslint-disable no-unused-expressions */ import EventEmitter from 'events'; import * as constants from '../../constants'; -import * as icons from '../../icons/icons'; import * as util from '../../util'; import Api from '../../api'; import BaseViewer from '../BaseViewer'; @@ -38,11 +37,13 @@ describe('lib/viewers/BaseViewer', () => { }, }); base.previewUI = { - replaceHeader: jest.fn(), + hideCrawler: jest.fn(), notification: { show: jest.fn(), hide: jest.fn(), }, + replaceHeader: jest.fn(), + setLoadingIcon: jest.fn(), }; }); @@ -54,10 +55,8 @@ describe('lib/viewers/BaseViewer', () => { describe('setup()', () => { test('should set options, a container, bind event listeners, and set timeout', () => { - const getIconFromExtensionStub = jest.spyOn(icons, 'getIconFromExtension'); jest.spyOn(base, 'addCommonListeners'); jest.spyOn(base, 'areAnnotationsEnabled').mockReturnValue(true); - jest.spyOn(base, 'finishLoadingSetup'); jest.spyOn(base, 'loadBoxAnnotations').mockResolvedValue(undefined); base.options.showAnnotations = true; base.options.enableAnnotationsDiscoverability = true; @@ -80,7 +79,6 @@ describe('lib/viewers/BaseViewer', () => { expect(base.containerEl).toHaveClass(constants.CLASS_BOX_PREVIEW_CONTENT); expect(base.containerEl).toHaveClass(constants.CLASS_ANNOTATIONS_DISCOVERABLE); expect(base.addCommonListeners).toBeCalled(); - expect(getIconFromExtensionStub).toBeCalled(); expect(typeof base.loadTimeout).toBe('number'); expect(base.annotatorPromise).toBeDefined(); expect(base.annotatorPromiseResolver).toBeDefined(); @@ -89,7 +87,6 @@ describe('lib/viewers/BaseViewer', () => { test('should add a mobile class to the container if on mobile', () => { base.isMobile = true; jest.spyOn(base, 'loadBoxAnnotations').mockResolvedValue(undefined); - jest.spyOn(base, 'finishLoadingSetup'); jest.spyOn(base, 'areAnnotationsEnabled').mockReturnValue(true); base.setup(); @@ -102,7 +99,6 @@ describe('lib/viewers/BaseViewer', () => { jest.spyOn(base, 'addCommonListeners'); jest.spyOn(base, 'areAnnotationsEnabled').mockReturnValue(false); jest.spyOn(base, 'loadBoxAnnotations').mockResolvedValue(undefined); - jest.spyOn(base, 'finishLoadingSetup'); base.options.showAnnotations = false; base.setup(); @@ -114,7 +110,6 @@ describe('lib/viewers/BaseViewer', () => { jest.spyOn(base, 'addCommonListeners'); jest.spyOn(base, 'areAnnotationsEnabled').mockReturnValue(true); jest.spyOn(base, 'loadBoxAnnotations').mockResolvedValue(undefined); - jest.spyOn(base, 'finishLoadingSetup'); base.options.sharedLink = 'url'; base.setup(); @@ -123,23 +118,14 @@ describe('lib/viewers/BaseViewer', () => { }); }); - describe('finishLoadingSetup()', () => { - test('should hide the crawler and set the file icon into the icon element', () => { - const container = { - classList: { - add: jest.fn(), - }, - innerHTML: '', - removeEventListener: jest.fn(), - }; - base.fileLoadingIcon = 'icon'; + describe('setupLoading()', () => { + test('should hide the crawler and set the file-specific loading icon', () => { + base.options.file = { extension: 'pdf' }; - jest.spyOn(containerEl, 'querySelector').mockReturnValue(container); + base.setupLoading(); - base.finishLoadingSetup(); - expect(container.innerHTML).toBe('icon'); - expect(container.classList.add).toBeCalled(); - base.options.container = null; + expect(base.previewUI.hideCrawler).toBeCalled(); + expect(base.previewUI.setLoadingIcon).toBeCalledWith('pdf'); }); }); @@ -620,7 +606,6 @@ describe('lib/viewers/BaseViewer', () => { test('should cleanup the base viewer', () => { jest.spyOn(base, 'loadAssets').mockResolvedValue(undefined); jest.spyOn(base, 'loadBoxAnnotations').mockResolvedValue(undefined); - jest.spyOn(base, 'finishLoadingSetup'); base.setup(); jest.spyOn(fullscreen, 'removeAllListeners').mockImplementation(); @@ -722,7 +707,6 @@ describe('lib/viewers/BaseViewer', () => { jest.spyOn(base, 'loadAssets').mockResolvedValue(undefined); jest.spyOn(base, 'areAnnotationsEnabled').mockReturnValue(false); jest.spyOn(base, 'loadBoxAnnotations').mockResolvedValue(undefined); - jest.spyOn(base, 'finishLoadingSetup'); base.setup(); event = { preventDefault: jest.fn(), diff --git a/src/lib/viewers/box3d/video360/__tests__/Video360Viewer-test.js b/src/lib/viewers/box3d/video360/__tests__/Video360Viewer-test.js index c17213c29..b99ca970e 100644 --- a/src/lib/viewers/box3d/video360/__tests__/Video360Viewer-test.js +++ b/src/lib/viewers/box3d/video360/__tests__/Video360Viewer-test.js @@ -59,7 +59,6 @@ describe('lib/viewers/box3d/video360/Video360Viewer', () => { describe('setup()', () => { beforeEach(() => { - jest.spyOn(viewer, 'finishLoadingSetup').mockImplementation(); viewer.setup(); }); @@ -409,8 +408,6 @@ describe('lib/viewers/box3d/video360/Video360Viewer', () => { getScene: jest.fn().mockReturnValue(scene), }; - jest.spyOn(viewer, 'finishLoadingSetup').mockImplementation(); - viewer.setup(); viewer.renderer = renderer; createPromise = viewer.create360Environment(); diff --git a/src/lib/viewers/error/PreviewErrorViewer.js b/src/lib/viewers/error/PreviewErrorViewer.js index 371f4b20b..06f64385d 100644 --- a/src/lib/viewers/error/PreviewErrorViewer.js +++ b/src/lib/viewers/error/PreviewErrorViewer.js @@ -44,7 +44,7 @@ class PreviewErrorViewer extends BaseViewer { * @override * @return {void} */ - finishLoadingSetup() { + setupLoading() { /* no op, custom loading logic for errors */ }