Skip to content

Commit

Permalink
Fix: Fullscreen classes are now removed when pressing escape (#927)
Browse files Browse the repository at this point in the history
  • Loading branch information
jstoffan committed Feb 14, 2019
1 parent 6997c94 commit 66ed344
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 123 deletions.
2 changes: 1 addition & 1 deletion src/index.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<html>
<head>
<meta name="viewport" content="width=device-width">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<link rel="stylesheet" href='./preview.css' />
<link rel="stylesheet" href='./annotations.css' />
<script src='https://cdn01.boxcdn.net/polyfills/core-js/2.5.3/core.min.js'></script>
Expand Down
89 changes: 45 additions & 44 deletions src/lib/Fullscreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -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]
*
Expand All @@ -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() {
Expand All @@ -52,7 +52,6 @@ class Fullscreen extends EventEmitter {
/**
* Returns true if the browser supports fullscreen natively
*
* @private
* @return {boolean} Fullscreen supported or not
*/
isSupported() {
Expand All @@ -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
Expand All @@ -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);
}
Expand Down
2 changes: 1 addition & 1 deletion src/lib/PageControls.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
128 changes: 59 additions & 69 deletions src/lib/__tests__/Fullscreen-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
});
});
});
Loading

0 comments on commit 66ed344

Please sign in to comment.