Skip to content

Commit

Permalink
feat(loading): Update loading experience to latest designs (#1345)
Browse files Browse the repository at this point in the history
  • Loading branch information
jstoffan authored Apr 5, 2021
1 parent 910b8a3 commit 5d248d5
Show file tree
Hide file tree
Showing 62 changed files with 194 additions and 668 deletions.
23 changes: 23 additions & 0 deletions src/lib/LoadingIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { PreviewLoading } from 'box-ui-elements/es/components/preview';

export type Options = {
containerEl: HTMLElement;
};

export default class LoadingIcon {
containerEl: HTMLElement;

constructor({ containerEl }: Options) {
this.containerEl = containerEl;
}

destroy(): void {
ReactDOM.unmountComponentAtNode(this.containerEl);
}

render(extension: string): void {
ReactDOM.render(<PreviewLoading extension={extension} />, this.containerEl);
}
}
2 changes: 1 addition & 1 deletion src/lib/Notification.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CLASS_HIDDEN, CLASS_BOX_PREVIEW_NOTIFICATION, CLASS_BOX_PREVIEW_NOTIFICATION_WRAPPER } from './constants';
import { ICON_CLOSE } from './icons/icons';
import { ICON_CLOSE } from './icons';

const HIDE_TIMEOUT_MS = 5000; // 5s

Expand Down
2 changes: 1 addition & 1 deletion src/lib/Popup.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CLASS_HIDDEN } from './constants';
import { ICON_CLOSE } from './icons/icons';
import { ICON_CLOSE } from './icons';
import { decodeKeydown } from './util';

class Popup {
Expand Down
6 changes: 1 addition & 5 deletions src/lib/Preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -871,6 +871,7 @@ class Preview extends EventEmitter {
this.ui.showNavigation(this.file.id, this.collection);

// Setup loading UI and progress bar
this.ui.showLoadingIcon(this.file.extension);
this.ui.showLoadingIndicator();
this.ui.startProgressBar();

Expand Down Expand Up @@ -1170,11 +1171,6 @@ class Preview extends EventEmitter {
throw new PreviewError(ERROR_CODE.PERMISSIONS_PREVIEW, __('error_permissions'));
}

// Show loading download button if user can download
if (canDownload(this.file, this.options)) {
this.ui.showLoadingDownloadButton(this.download);
}

// Determine the asset loader to use
const loader = this.getLoader(this.file);

Expand Down
137 changes: 38 additions & 99 deletions src/lib/PreviewUI.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,27 @@
import LoadingIcon from './LoadingIcon';
import Notification from './Notification';
import ProgressBar from './ProgressBar';
import shellTemplate from './shell.html';
import Notification from './Notification';
import {
CLASS_BOX_PREVIEW_BASE_HEADER,
CLASS_BOX_PREVIEW_HAS_HEADER,
CLASS_BOX_PREVIEW_HAS_NAVIGATION,
CLASS_BOX_PREVIEW_HEADER,
CLASS_BOX_PREVIEW_THEME_DARK,
CLASS_HIDDEN,
CLASS_INVISIBLE,
CLASS_PREVIEW_LOADED,
SELECTOR_BOX_PREVIEW_BTN_DOWNLOAD,
SELECTOR_BOX_PREVIEW_BTN_LOADING_DOWNLOAD,
SELECTOR_BOX_PREVIEW_BTN_PRINT,
SELECTOR_BOX_PREVIEW_CONTAINER,
SELECTOR_BOX_PREVIEW_CRAWLER_WRAPPER,
SELECTOR_BOX_PREVIEW_HEADER_CONTAINER,
SELECTOR_BOX_PREVIEW_LOADING_TEXT,
SELECTOR_BOX_PREVIEW_LOADING_WRAPPER,
SELECTOR_BOX_PREVIEW_LOGO_CUSTOM,
SELECTOR_BOX_PREVIEW_LOGO_DEFAULT,
SELECTOR_BOX_PREVIEW,
SELECTOR_NAVIGATION_LEFT,
SELECTOR_NAVIGATION_RIGHT,
SELECTOR_BOX_PREVIEW_CONTENT,
SELECTOR_BOX_PREVIEW_ICON,
SELECTOR_NAVIGATION_LEFT,
SELECTOR_NAVIGATION_RIGHT,
} from './constants';
import { getIconFromName, getIconFromExtension } from './icons/icons';
import { insertTemplate } from './util';

class PreviewUI {
Expand All @@ -48,6 +43,9 @@ class PreviewUI {
/** @property {Function} - Keydown handler */
keydownHandler;

/** @property {LoadingIcon} - Loading icon instance */
loadingIcon;

/** @property {HTMLElement} - Preview container element which houses the sidebar and content */
previewContainer;

Expand All @@ -69,6 +67,11 @@ class PreviewUI {
this.previewContainer.removeEventListener('mousemove', this.mousemoveHandler);
}

if (this.loadingIcon) {
this.loadingIcon.destroy();
this.loadingIcon = null;
}

if (this.container) {
this.container.innerHTML = '';
}
Expand Down Expand Up @@ -123,18 +126,16 @@ class PreviewUI {
this.setupHeader(options.header, options.logoUrl);
}

// Destroy the loading icon if disabled
if (options.showLoading === false) {
this.destroyLoading();
}

// Setup progress bar
if (options.showProgress) {
this.progressBar = new ProgressBar(this.container);
}

// Setup loading indicator
if (options.showLoading) {
this.setupLoading();
} else {
this.destroyLoading();
}

// Attach keyboard events
document.addEventListener('keydown', this.keydownHandler);

Expand Down Expand Up @@ -248,25 +249,7 @@ class PreviewUI {
}

/**
* Shows the loading download button if the viewers implement download
*
* @public
* @param {Function} handler - Download click handler
* @return {void}
*/
showLoadingDownloadButton(handler) {
const downloadButtonEl = this.container.querySelector(SELECTOR_BOX_PREVIEW_BTN_LOADING_DOWNLOAD);
if (!downloadButtonEl) {
return;
}

downloadButtonEl.title = __('download');
downloadButtonEl.classList.remove(CLASS_INVISIBLE);
downloadButtonEl.addEventListener('click', handler);
}

/**
* Shows the loading indicator.
* Shows the loading indicator
*
* @public
* @return {void}
Expand All @@ -289,47 +272,6 @@ 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);
}
}

/**
* 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');
}
}

/**
Expand Down Expand Up @@ -420,22 +362,38 @@ class PreviewUI {
headerToShow.classList.remove(CLASS_HIDDEN);
}

/**
* Sets the preview loading icon
*
* @public
* @param {string} extension - File extension to use for loading icon
* @return {void}
*/
showLoadingIcon(extension) {
const iconEl = this.container.querySelector(SELECTOR_BOX_PREVIEW_ICON);

if (iconEl) {
this.loadingIcon = this.loadingIcon || new LoadingIcon({ containerEl: iconEl });
this.loadingIcon.render(extension);
}
}

//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
/**
* Remove global loading indicators from the shell
* Remove global loading indicator from the shell
*
* @private
* @return {void}
*/
destroyLoading() {
const loadingWrapperEl = this.container.querySelector(SELECTOR_BOX_PREVIEW_LOADING_WRAPPER);
if (!loadingWrapperEl) {
const loadingIconEl = this.container.querySelector(SELECTOR_BOX_PREVIEW_ICON);
if (!loadingIconEl) {
return;
}

loadingWrapperEl.parentElement.removeChild(loadingWrapperEl);
loadingIconEl.parentElement.removeChild(loadingIconEl);
}

/**
Expand Down Expand Up @@ -469,25 +427,6 @@ class PreviewUI {
customLogoEl.classList.remove(CLASS_HIDDEN);
}
}

/**
* Sets up preview loading indicator.
*
* @private
* @return {void}
*/
setupLoading() {
const loadingWrapperEl = this.container.querySelector(SELECTOR_BOX_PREVIEW_LOADING_WRAPPER);
if (!loadingWrapperEl) {
return;
}

const loadingTextEl = loadingWrapperEl.querySelector(SELECTOR_BOX_PREVIEW_LOADING_TEXT);
loadingTextEl.textContent = __('loading_preview');

const loadingDownloadButtonEl = loadingWrapperEl.querySelector(SELECTOR_BOX_PREVIEW_BTN_LOADING_DOWNLOAD);
loadingDownloadButtonEl.textContent = __('download_file');
}
}

export default PreviewUI;
14 changes: 1 addition & 13 deletions src/lib/__tests__/Preview-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1216,6 +1216,7 @@ describe('lib/Preview', () => {
test('should setup the shell, update navigation, and show loading/start progress', () => {
const previewUIMock = sandbox.mock(preview.ui);
previewUIMock.expects('setup');
previewUIMock.expects('showLoadingIcon');
previewUIMock.expects('showLoadingIndicator');
previewUIMock.expects('startProgressBar');
previewUIMock.expects('showNavigation');
Expand Down Expand Up @@ -1666,7 +1667,6 @@ describe('lib/Preview', () => {
stubs.destroy = jest.spyOn(preview, 'destroy');
stubs.checkPermission = jest.spyOn(file, 'checkPermission').mockReturnValue(true);
stubs.canDownload = jest.spyOn(file, 'canDownload').mockReturnValue(false);
stubs.showLoadingDownloadButton = jest.spyOn(preview.ui, 'showLoadingDownloadButton').mockImplementation();
stubs.loadPromiseResolve = Promise.resolve();
stubs.determineRepresentationStatusPromise = Promise.resolve();
stubs.loader = {
Expand Down Expand Up @@ -1711,18 +1711,6 @@ describe('lib/Preview', () => {
expect(() => preview.loadViewer()).toThrowError(PreviewError);
});

test('should show the loading download button if file can be downloaded', () => {
stubs.canDownload.mockReturnValue(true);
preview.loadViewer({});
expect(stubs.showLoadingDownloadButton).toBeCalled();
});

test("should not show the loading download button if file can't be downloaded", () => {
stubs.canDownload.mockReturnValue(false);
preview.loadViewer({});
expect(stubs.showLoadingDownloadButton).not.toBeCalled();
});

test('should throw an unsupported error if there is no loader for general file types', () => {
stubs.getLoader.mockReturnValue(undefined);

Expand Down
Loading

0 comments on commit 5d248d5

Please sign in to comment.