From b045ef043becac5a683f82c2f9d5d2b0a1bbb693 Mon Sep 17 00:00:00 2001 From: Jared Stoffan Date: Wed, 13 Feb 2019 12:52:16 -0800 Subject: [PATCH] Fix: Fullscreen classes are now removed when pressing escape --- src/index.html | 2 +- src/lib/Fullscreen.js | 89 ++++++------ src/lib/PageControls.js | 2 +- src/lib/__tests__/Fullscreen-test.js | 128 ++++++++---------- .../viewers/box3d/video360/Video360Viewer.js | 8 -- 5 files changed, 106 insertions(+), 123 deletions(-) diff --git a/src/index.html b/src/index.html index 4d94d29df..b1f912fe3 100644 --- a/src/index.html +++ b/src/index.html @@ -1,7 +1,7 @@ - + diff --git a/src/lib/Fullscreen.js b/src/lib/Fullscreen.js index 8570b5fe2..73781a146 100644 --- a/src/lib/Fullscreen.js +++ b/src/lib/Fullscreen.js @@ -3,6 +3,9 @@ import fscreen from 'fscreen'; import { CLASS_FULLSCREEN, CLASS_FULLSCREEN_UNSUPPORTED } from './constants'; class Fullscreen extends EventEmitter { + /** {HTMLElement} - The element used as the root for fullscreen mode */ + fullscreenElement; + /** * [constructor] * @@ -17,31 +20,28 @@ class Fullscreen extends EventEmitter { /** * Binds DOM listeners for Fullscreen. * - * @protected * @return {void} */ bindDOMListeners() { - // as of now (1/12/18) fullscreenchange is not universally adopted, fscreen will - // detect and add the appropriate vendor prefixed event + // The fullscreenchange event is not universally supported, but fscreen will + // detect and add the appropriate vendor-prefixed event fscreen.addEventListener('fullscreenchange', this.fullscreenchangeHandler); } /** * Unbinds DOM listeners for Fullscreen. * - * @protected * @return {void} */ unbindDOMListeners() { - // as of now (1/12/18) fullscreenchange is not universally adopted, fscreen will - // detect and add the appropriate vendor prefixed event + // The fullscreenchange event is not universally supported, but fscreen will + // detect and add the appropriate vendor-prefixed event fscreen.removeEventListener('fullscreenchange', this.fullscreenchangeHandler); } /** * [destructor] * - * @protected * @return {void} */ destroy() { @@ -52,7 +52,6 @@ class Fullscreen extends EventEmitter { /** * Returns true if the browser supports fullscreen natively * - * @private * @return {boolean} Fullscreen supported or not */ isSupported() { @@ -62,45 +61,62 @@ class Fullscreen extends EventEmitter { /** * Return true if full screen is active * - * @public - * @param {HTMLElement} [element] - fullscreen element + * @param {HTMLElement} [el] - fullscreen element * @return {boolean} In fullscreen or not */ - isFullscreen(element) { + isFullscreen(el) { if (this.isSupported()) { return !!fscreen.fullscreenElement; } - return element instanceof HTMLElement && element.classList.contains(CLASS_FULLSCREEN); + return el && el.classList.contains(CLASS_FULLSCREEN); } /** - * Fires events when the fullscreen state changes + * Handles fullscreen change events from fscreen * - * @private * @return {void} */ fullscreenchangeHandler = () => { - if (this.isFullscreen()) { - this.focusFullscreenElement(); - this.emit('enter'); + const { fullscreenElement } = fscreen; + + if (fullscreenElement) { + this.fullscreenEnterHandler(fullscreenElement); } else { - this.emit('exit'); + this.fullscreenExitHandler(); } }; /** - * Focuses the element + * Handles fullscreen enter events * - * @private * @param {HTMLElement} el - fullscreen element * @return {void} */ - focusFullscreenElement = () => { - // Focus on the fullscreen element so keyboard - // events are triggered without an extra click - fscreen.fullscreenElement.focus(); - }; + fullscreenEnterHandler(el) { + this.fullscreenElement = el; + this.fullscreenElement.classList.add(CLASS_FULLSCREEN); + this.fullscreenElement.focus(); + + if (!this.isSupported()) { + this.fullscreenElement.classList.add(CLASS_FULLSCREEN_UNSUPPORTED); + } + + this.emit('enter'); + } + + /** + * Handles fullscreen exit events + * + * @return {void} + */ + fullscreenExitHandler() { + this.fullscreenElement.classList.remove(CLASS_FULLSCREEN); + this.fullscreenElement.classList.remove(CLASS_FULLSCREEN_UNSUPPORTED); + this.fullscreenElement = null; + + this.emit('exit'); + } /** * Enter fullscreen mode @@ -109,50 +125,35 @@ class Fullscreen extends EventEmitter { * @return {void} */ enter(el = document.documentElement) { - if (el instanceof HTMLElement) { - el.classList.add(CLASS_FULLSCREEN); - - if (!this.isSupported()) { - el.classList.add(CLASS_FULLSCREEN_UNSUPPORTED); - } - } - if (this.isSupported()) { fscreen.requestFullscreenFunction(el).call(el, Element.ALLOW_KEYBOARD_INPUT); } else { - this.emit('enter'); + this.fullscreenEnterHandler(el); } } /** * Exit fullscreen mode * - * @param {HTMLElement} el - fullscreen element * @return {void} */ - exit(el = document.documentElement) { - if (el instanceof HTMLElement) { - el.classList.remove(CLASS_FULLSCREEN); - el.classList.remove(CLASS_FULLSCREEN_UNSUPPORTED); - } - + exit() { if (this.isSupported()) { fscreen.exitFullscreen(); } else { - this.emit('exit'); + this.fullscreenExitHandler(); } } /** * Toggle fullscreen mode * - * @public * @param {HTMLElement} el - fullscreen element * @return {void} */ toggle(el = document.documentElement) { if (this.isFullscreen(el)) { - this.exit(el); + this.exit(); } else { this.enter(el); } diff --git a/src/lib/PageControls.js b/src/lib/PageControls.js index 1b027878e..15d6defdc 100644 --- a/src/lib/PageControls.js +++ b/src/lib/PageControls.js @@ -132,7 +132,7 @@ class PageControls extends EventEmitter { const nextPageButtonEl = this.controlsEl.querySelector(`.${NEXT_PAGE}`); // Safari disables keyboard input in fullscreen before Safari 10.1 - const isSafariFullscreen = Browser.getName() === 'Safari' && fullscreen.isFullscreen(this.controlsEl); + const isSafariFullscreen = Browser.getName() === 'Safari' && fullscreen.isFullscreen(); // Disable page number selector if there is only one page or less if (pageNumButtonEl) { diff --git a/src/lib/__tests__/Fullscreen-test.js b/src/lib/__tests__/Fullscreen-test.js index 6c5ae6fb1..aec5e8f66 100644 --- a/src/lib/__tests__/Fullscreen-test.js +++ b/src/lib/__tests__/Fullscreen-test.js @@ -39,121 +39,111 @@ describe('lib/Fullscreen', () => { }); }); - describe('fullscreenchangeHandler()', () => { - before(() => { - fixture.setBase('src/lib'); - }); - - beforeEach(() => { - fixture.load('__tests__/Fullscreen-test.html'); - }); - - afterEach(() => { - fixture.cleanup(); - }); - - it('should emit enter if we are entering fullscreen and if true fullscreen is supported', () => { - sandbox.stub(fullscreen, 'isSupported').returns(true); - sandbox.stub(fullscreen, 'isFullscreen').returns(true); + describe('fullscreenEnterHandler()', () => { + it('should add the fullscreen class and focus the element', () => { + const element = document.createElement('div'); + sandbox.stub(element, 'focus'); sandbox.stub(fullscreen, 'emit'); - sandbox.stub(fullscreen, 'focusFullscreenElement'); - fullscreen.fullscreenchangeHandler(); + fullscreen.fullscreenEnterHandler(element); + expect(element.classList.contains(CLASS_FULLSCREEN)).to.be.true; + expect(element.focus).to.have.been.called; expect(fullscreen.emit).to.have.been.calledWith('enter'); - expect(fullscreen.focusFullscreenElement).to.have.been.called; }); + }); - it('should emit exit if we are exiting fullscreen and if true fullscreen is supported', () => { - sandbox.stub(fullscreen, 'isSupported').returns(true); - sandbox.stub(fullscreen, 'isFullscreen').returns(false); + describe('fullscreenExitHandler()', () => { + it('should remove the fullscreen class and not focus the element', () => { + const element = document.createElement('div'); + element.classList.add(CLASS_FULLSCREEN); + sandbox.stub(element, 'focus'); sandbox.stub(fullscreen, 'emit'); - sandbox.stub(fullscreen, 'focusFullscreenElement'); + sandbox.stub(fullscreen, 'fullscreenElement').value(element); - fullscreen.fullscreenchangeHandler(); + fullscreen.fullscreenExitHandler(); + expect(element.classList.contains(CLASS_FULLSCREEN)).to.be.false; + expect(element.focus).not.to.have.been.called; expect(fullscreen.emit).to.have.been.calledWith('exit'); - expect(fullscreen.focusFullscreenElement).not.to.have.been.called; }); + }); - it('should emit enter if we are entering fullscreen and if true fullscreen is not supported', () => { - sandbox.stub(fullscreen, 'isSupported').returns(false); + describe('enter()', () => { + beforeEach(() => { + sandbox.stub(fullscreen, 'fullscreenEnterHandler'); sandbox.stub(fullscreen, 'isFullscreen').returns(true); - sandbox.stub(fullscreen, 'emit'); - sandbox.stub(fullscreen, 'focusFullscreenElement'); - - fullscreen.fullscreenchangeHandler(); - - expect(fullscreen.emit).to.have.been.calledWith('enter'); }); - it('should emit exit if we are exiting fullscreen and if true fullscreen is not supported', () => { - sandbox.stub(fullscreen, 'isSupported').returns(false); - sandbox.stub(fullscreen, 'isFullscreen').returns(false); - sandbox.stub(fullscreen, 'emit'); - - fullscreen.fullscreenchangeHandler(); - - expect(fullscreen.emit).to.have.been.calledWith('exit'); - }); - }); + it('should trigger native requestFullscreen handler if not in fullscreen and true fullscreen is supported', () => { + const fullscreenStub = sandbox.stub(); + sandbox.stub(fscreen, 'requestFullscreenFunction').returns(fullscreenStub); + sandbox.stub(fullscreen, 'isSupported').returns(true); - describe('enter', () => { - it('should add the fullscreen class', () => { const element = document.createElement('div'); - fullscreen.enter(element); - expect(element.classList.contains(CLASS_FULLSCREEN)).to.be.true; + expect(fullscreenStub).to.have.been.calledWith(Element.ALLOW_KEYBOARD_INPUT); }); - }); - describe('exit', () => { - it('should remove the fullscreen class', () => { - const element = document.createElement('div'); - element.classList.add(CLASS_FULLSCREEN); + it('should trigger the fullscreenEnterHandler immediately if true fullscreen is not supported', () => { + sandbox.stub(fullscreen, 'isSupported').returns(false); + const element = document.createElement('div'); fullscreen.enter(element); - expect(element.classList.contains(CLASS_FULLSCREEN)).to.be.true; + expect(fullscreen.fullscreenEnterHandler).to.have.been.called; }); }); - describe('toggle()', () => { + describe('exit()', () => { + beforeEach(() => { + sandbox.stub(fullscreen, 'fullscreenElement').value(document.createElement('div')); + sandbox.stub(fullscreen, 'fullscreenExitHandler'); + sandbox.stub(fullscreen, 'isFullscreen').returns(true); + }); + it('should trigger native exitFullscreen handler if in fullscreen and true fullscreen is supported', () => { const exitFullscreen = sinon.stub(); sandbox.stub(fscreen, 'exitFullscreen').value(exitFullscreen); sandbox.stub(fullscreen, 'isSupported').returns(true); - sandbox.stub(fullscreen, 'isFullscreen').returns(true); - fullscreen.toggle({}); + fullscreen.exit(); expect(exitFullscreen).to.have.been.called; }); - it('should trigger native requestFullscreen handler if not in fullscreen and true fullscreen is supported', () => { + it('should trigger the fullscreenExitHandler immediately if true fullscreen is not supported', () => { + sandbox.stub(fullscreen, 'isSupported').returns(false); + + fullscreen.exit(); + + expect(fullscreen.fullscreenExitHandler).to.have.been.called; + }); + }); + + describe('toggle()', () => { + beforeEach(() => { + sandbox.stub(fullscreen, 'enter'); + sandbox.stub(fullscreen, 'exit'); + sandbox.stub(fullscreen, 'isSupported').returns(false); + }); + + it('should call enter if not already in fullscreen', () => { const element = document.createElement('div'); - const fullscreenStub = sandbox.stub(); - sandbox.stub(fscreen, 'requestFullscreenFunction').returns(fullscreenStub); - sandbox.stub(fullscreen, 'isSupported').returns(true); - sandbox.stub(fullscreen, 'isFullscreen').returns(false); fullscreen.toggle(element); - expect(fullscreenStub).to.have.been.calledWith(Element.ALLOW_KEYBOARD_INPUT); + expect(fullscreen.enter).to.have.been.called; }); - }); - describe('focusFullscreenElement()', () => { - it('should focus the element when event is passed in', () => { + it('should call exit if already in fullscreen', () => { const element = document.createElement('div'); - sandbox.stub(element, 'focus'); - sandbox.stub(fscreen, 'fullscreenElement').value(element); + element.classList.add(CLASS_FULLSCREEN); fullscreen.toggle(element); - fullscreen.focusFullscreenElement(); - expect(fscreen.fullscreenElement.focus.called).to.be.true; + expect(fullscreen.exit).to.have.been.called; }); }); }); diff --git a/src/lib/viewers/box3d/video360/Video360Viewer.js b/src/lib/viewers/box3d/video360/Video360Viewer.js index 7efaf340e..aeea1fd09 100644 --- a/src/lib/viewers/box3d/video360/Video360Viewer.js +++ b/src/lib/viewers/box3d/video360/Video360Viewer.js @@ -1,5 +1,4 @@ /* global BoxSDK */ -import fullscreen from '../../../Fullscreen'; import DashViewer from '../../media/DashViewer'; import Video360Controls from './Video360Controls'; import Video360Renderer from './Video360Renderer'; @@ -221,13 +220,6 @@ class Video360Viewer extends DashViewer { }); } - /** - * @inheritdoc - */ - toggleFullscreen() { - fullscreen.toggle(this.wrapperEl); - } - /** * Handles toggle VR event *