diff --git a/package.json b/package.json index 34345dce..5bfddc2a 100644 --- a/package.json +++ b/package.json @@ -83,6 +83,9 @@ "vinyl-source-stream": "^2.0.0" }, "lint-staged": { - "*.js": "eslint --fix" + "*.js": [ + "prettier --write", + "eslint --fix" + ] } } diff --git a/src/button.js b/src/button.js index ac78c34e..ff7ca0fa 100644 --- a/src/button.js +++ b/src/button.js @@ -1,7 +1,12 @@ import { SpriteClass } from './sprite.js'; import Text from './text.js'; import { track } from './pointer.js'; -import { srOnlyStyle, noop, addToDom } from './utils.js'; +import { + srOnlyStyle, + scrollParams, + noop, + addToDom +} from './utils.js'; /** * An accessible button. Supports screen readers and keyboard navigation using the Tab key. The button is automatically [tracked](api/pointer#track) by the pointer and accepts all pointer functions, but you will still need to call [initPointer](api/pointer#initPointer) to have pointer events enabled. @@ -189,7 +194,8 @@ class Button extends SpriteClass { */ this.focused = true; // prevent infinite loop - if (document.activeElement != this._dn) this._dn.focus(); + if (document.activeElement != this._dn) + this._dn.focus(scrollParams); this.onFocus(); } diff --git a/src/scene.js b/src/scene.js index 67c13241..30d2c648 100644 --- a/src/scene.js +++ b/src/scene.js @@ -1,7 +1,12 @@ import { getContext } from './core.js'; import { GameObjectClass } from './gameObject.js'; import { on, off } from './events.js'; -import { srOnlyStyle, addToDom, removeFromArray } from './utils.js'; +import { + srOnlyStyle, + scrollParams, + addToDom, + removeFromArray +} from './utils.js'; import { collides } from './helpers.js'; /** @@ -201,13 +206,13 @@ class Scene { x, y, width, - height, + height }); if (!this._dn.isConnected) { addToDom(this._dn, canvas); } - } + }; if (this.context) { this._i(); @@ -280,9 +285,9 @@ class Scene { // find first focusable object let focusableObject = this._o.find(object => object.focus); if (focusableObject) { - focusableObject.focus(); + focusableObject.focus(scrollParams); } else { - this._dn.focus(); + this._dn.focus(scrollParams); } this.onShow(); diff --git a/src/utils.js b/src/utils.js index 24bcf33b..38f63406 100644 --- a/src/utils.js +++ b/src/utils.js @@ -3,6 +3,8 @@ export let noop = () => {}; // style used for DOM nodes needed for screen readers export let srOnlyStyle = 'position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);'; +// prevent focus from scrolling the page +export let scrollParams = { preventScroll: true }; /** * Append a node directly after the canvas and as the last element of other kontra nodes. diff --git a/test/unit/button.spec.js b/test/unit/button.spec.js index 926f989f..623aa4ab 100644 --- a/test/unit/button.spec.js +++ b/test/unit/button.spec.js @@ -316,7 +316,11 @@ describe('button', () => { sinon.spy(button._dn, 'focus'); button.focus(); - expect(button._dn.focus.called).to.be.true; + expect( + button._dn.focus.calledWith( + sinon.match({ preventScroll: true }) + ) + ).to.be.true; }); it('should not focus the DOM node if it is already focused', () => { diff --git a/test/unit/scene.spec.js b/test/unit/scene.spec.js index 7a0ee7bc..d20d546c 100644 --- a/test/unit/scene.spec.js +++ b/test/unit/scene.spec.js @@ -1,5 +1,10 @@ import Scene, { SceneClass } from '../../src/scene.js'; -import { _reset, init, getContext, getCanvas } from '../../src/core.js'; +import { + _reset, + init, + getContext, + getCanvas +} from '../../src/core.js'; import { emit } from '../../src/events.js'; import { noop, srOnlyStyle } from '../../src/utils.js'; import { collides } from '../../src/helpers.js'; @@ -219,9 +224,15 @@ describe('scene', () => { }); it('should focus the DOM node', () => { + sinon.spy(scene._dn, 'focus'); scene.show(); expect(document.activeElement).to.equal(scene._dn); + expect( + scene._dn.focus.calledWith( + sinon.match({ preventScroll: true }) + ) + ).to.be.true; }); it('should focus the first focusable object', () => { @@ -231,7 +242,9 @@ describe('scene', () => { scene.add(object); scene.show(); - expect(object.focus.called).to.be.true; + expect( + object.focus.calledWith(sinon.match({ preventScroll: true })) + ).to.be.true; }); it('should call onShow', () => {